feat: Better thread handling

This commit is contained in:
Jakub Žitník 2025-03-12 09:41:13 +01:00
parent ed921ff6cd
commit 4041c8f7d2
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
8 changed files with 124 additions and 18 deletions

View File

@ -3,9 +3,7 @@ package cz.jzitnik;
import cz.jzitnik.game.GameSaver; import cz.jzitnik.game.GameSaver;
import cz.jzitnik.game.logic.CustomLogicProvider; import cz.jzitnik.game.logic.CustomLogicProvider;
import cz.jzitnik.game.mobs.EntityLogicProvider; import cz.jzitnik.game.mobs.EntityLogicProvider;
import cz.jzitnik.game.threads.HealthRegenerationThread; import cz.jzitnik.game.threads.ThreadProvider;
import cz.jzitnik.game.threads.HungerDrainThread;
import cz.jzitnik.game.threads.InputHandlerThread;
import cz.jzitnik.game.ui.*; import cz.jzitnik.game.ui.*;
import cz.jzitnik.game.SpriteLoader; import cz.jzitnik.game.SpriteLoader;
import cz.jzitnik.tui.ScreenRenderer; import cz.jzitnik.tui.ScreenRenderer;
@ -32,16 +30,11 @@ public class Main {
final boolean[] isRunning = { true }; final boolean[] isRunning = { true };
Thread inputHandlerThread = new InputHandlerThread(game, terminal, screenRenderer, isRunning);
Thread healingThread = new HealthRegenerationThread(game.getPlayer());
Thread hungerDrainThread = new HungerDrainThread(game.getPlayer());
EntityLogicProvider entityLogicProvider = new EntityLogicProvider(); EntityLogicProvider entityLogicProvider = new EntityLogicProvider();
CustomLogicProvider customLogicProvider = new CustomLogicProvider(); CustomLogicProvider customLogicProvider = new CustomLogicProvider();
// Start all threads ThreadProvider threadProvider = new ThreadProvider(game, screenRenderer, terminal, isRunning);
healingThread.start(); threadProvider.start();
hungerDrainThread.start();
inputHandlerThread.start();
while (isRunning[0]) { while (isRunning[0]) {
try { try {

View File

@ -0,0 +1,11 @@
package cz.jzitnik.game.annotations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ThreadAnnotation {
}

View File

@ -6,9 +6,6 @@ import java.util.Set;
import cz.jzitnik.game.Game; import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.EntitySpawn; import cz.jzitnik.game.annotations.EntitySpawn;
import cz.jzitnik.game.entities.Block;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.jline.terminal.Terminal; import org.jline.terminal.Terminal;
import org.reflections.Reflections; import org.reflections.Reflections;
@ -21,7 +18,6 @@ public class EntitySpawnProvider {
int playerY = playerLocation[1]; int playerY = playerLocation[1];
for (EntitySpawnInterface entitySpawnInterface : spawnList) { for (EntitySpawnInterface entitySpawnInterface : spawnList) {
entitySpawnInterface.spawn(playerX, playerY, game, terminal); entitySpawnInterface.spawn(playerX, playerY, game, terminal);
} }
} }

View File

@ -0,0 +1,74 @@
package cz.jzitnik.game.threads;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.ThreadAnnotation;
import cz.jzitnik.game.entities.Player;
import cz.jzitnik.tui.ScreenRenderer;
import org.jline.terminal.Terminal;
import org.reflections.Reflections;
public class ThreadProvider {
private final Game game;
private final ScreenRenderer screenRenderer;
private final Terminal terminal;
private final boolean[] isRunning;
private final List<Thread> list = new ArrayList<>();
public void start() {
for (Thread thread : list) {
thread.start();
}
}
public ThreadProvider(Game game, ScreenRenderer screenRenderer, Terminal terminal, boolean[] isRunning) {
this.game = game;
this.screenRenderer = screenRenderer;
this.terminal = terminal;
this.isRunning = isRunning;
registerHandlers();
}
private void registerHandlers() {
Reflections reflections = new Reflections("cz.jzitnik.game.threads");
Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(ThreadAnnotation.class);
for (Class<?> clazz : handlerClasses) {
if (Thread.class.isAssignableFrom(clazz)) {
try {
for (var constructor : clazz.getDeclaredConstructors()) {
var paramTypes = constructor.getParameterTypes();
var params = new Object[paramTypes.length];
boolean suitable = true;
for (int i = 0; i < paramTypes.length; i++) {
Class<?> type = paramTypes[i];
if (type == Game.class) params[i] = game;
else if (type == ScreenRenderer.class) params[i] = screenRenderer;
else if (type == Terminal.class) params[i] = terminal;
else if (type == boolean[].class) params[i] = isRunning;
else if (type == Player.class) params[i] = game.getPlayer();
else {
suitable = false;
break;
}
}
if (suitable) {
constructor.setAccessible(true);
Thread instance = (Thread) constructor.newInstance(params);
list.add(instance);
break; // Found a matching constructor, go to next class
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -1,9 +1,11 @@
package cz.jzitnik.game.threads; package cz.jzitnik.game.threads.list;
import cz.jzitnik.game.annotations.ThreadAnnotation;
import cz.jzitnik.game.entities.Player; import cz.jzitnik.game.entities.Player;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@ThreadAnnotation
public class HealthRegenerationThread extends Thread { public class HealthRegenerationThread extends Thread {
private final Player player; private final Player player;

View File

@ -1,9 +1,11 @@
package cz.jzitnik.game.threads; package cz.jzitnik.game.threads.list;
import cz.jzitnik.game.annotations.ThreadAnnotation;
import cz.jzitnik.game.entities.Player; import cz.jzitnik.game.entities.Player;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@AllArgsConstructor @AllArgsConstructor
@ThreadAnnotation
public class HungerDrainThread extends Thread { public class HungerDrainThread extends Thread {
private final Player player; private final Player player;

View File

@ -1,4 +1,4 @@
package cz.jzitnik.game.threads; package cz.jzitnik.game.threads.list;
import cz.jzitnik.game.Game; import cz.jzitnik.game.Game;
import cz.jzitnik.game.blocks.Chest; import cz.jzitnik.game.blocks.Chest;
@ -9,8 +9,11 @@ import cz.jzitnik.tui.ScreenRenderer;
import org.jline.terminal.MouseEvent; import org.jline.terminal.MouseEvent;
import org.jline.terminal.Terminal; import org.jline.terminal.Terminal;
import java.io.IOException; import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.util.Optional; import java.util.Optional;
import cz.jzitnik.game.annotations.ThreadAnnotation;
@ThreadAnnotation
public class InputHandlerThread extends Thread { public class InputHandlerThread extends Thread {
private final Game game; private final Game game;
private final Terminal terminal; private final Terminal terminal;
@ -91,7 +94,7 @@ public class InputHandlerThread extends Thread {
} }
} }
} }
} catch (IOException e) { } catch (IOException | BufferUnderflowException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }

View File

@ -0,0 +1,25 @@
package cz.jzitnik.game.threads.list;
import cz.jzitnik.game.annotations.ThreadAnnotation;
import cz.jzitnik.game.entities.Player;
import lombok.AllArgsConstructor;
@AllArgsConstructor
@ThreadAnnotation
public class NoHungerThread extends Thread {
private final Player player;
@Override
public void run() {
while (true) {
try {
Thread.sleep(6000);
if (player.getHunger() == 0) {
player.setHealth(player.getHunger() - 1);
}
} catch (InterruptedException e) {
break;
}
}
}
}