feat: Started implementing screen rendering
And some minor design changes and stuff like that.
This commit is contained in:
@@ -8,11 +8,16 @@ import cz.jzitnik.annotations.Dependency;
|
|||||||
import cz.jzitnik.annotations.injectors.InjectDependency;
|
import cz.jzitnik.annotations.injectors.InjectDependency;
|
||||||
import cz.jzitnik.events.KeyboardPressEvent;
|
import cz.jzitnik.events.KeyboardPressEvent;
|
||||||
import cz.jzitnik.events.MouseAction;
|
import cz.jzitnik.events.MouseAction;
|
||||||
|
import cz.jzitnik.events.RerenderScreen;
|
||||||
|
import cz.jzitnik.events.TerminalResizeEvent;
|
||||||
import cz.jzitnik.states.RunningState;
|
import cz.jzitnik.states.RunningState;
|
||||||
import cz.jzitnik.utils.StateManager;
|
import cz.jzitnik.utils.StateManager;
|
||||||
import cz.jzitnik.utils.events.EventManager;
|
import cz.jzitnik.utils.events.EventManager;
|
||||||
import lombok.SneakyThrows;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Dependency
|
@Dependency
|
||||||
public class Cli implements Runnable {
|
public class Cli implements Runnable {
|
||||||
@InjectDependency
|
@InjectDependency
|
||||||
@@ -21,22 +26,24 @@ public class Cli implements Runnable {
|
|||||||
@InjectDependency
|
@InjectDependency
|
||||||
private StateManager stateManager;
|
private StateManager stateManager;
|
||||||
|
|
||||||
@SneakyThrows // I know deal with it
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
eventManager.start(); // Start event manager thread
|
eventManager.start(); // Start event manager thread
|
||||||
|
|
||||||
TerminalScreen terminal = new DefaultTerminalFactory()
|
try (TerminalScreen terminal = new DefaultTerminalFactory()
|
||||||
.setMouseCaptureMode(MouseCaptureMode.CLICK_RELEASE_DRAG_MOVE)
|
.setMouseCaptureMode(MouseCaptureMode.CLICK_RELEASE_DRAG_MOVE)
|
||||||
.createScreen();
|
.createScreen()) {
|
||||||
|
stateManager.registerManually(terminal);
|
||||||
|
|
||||||
terminal.setCursorPosition(null);
|
terminal.setCursorPosition(null);
|
||||||
terminal.doResizeIfNecessary();
|
terminal.doResizeIfNecessary();
|
||||||
terminal.getTerminal().enterPrivateMode();
|
terminal.getTerminal().enterPrivateMode();
|
||||||
|
|
||||||
|
terminal.getTerminal().addResizeListener((_, terminalSize) -> eventManager.emitEvent(new TerminalResizeEvent(terminalSize)));
|
||||||
|
|
||||||
RunningState runningState = stateManager.getOrThrow(RunningState.class);
|
RunningState runningState = stateManager.getOrThrow(RunningState.class);
|
||||||
while (runningState.isRunning()) {
|
while (runningState.isRunning()) {
|
||||||
KeyStroke keyStroke = terminal.pollInput();
|
KeyStroke keyStroke = terminal.readInput();
|
||||||
if (keyStroke != null) {
|
if (keyStroke != null) {
|
||||||
if (keyStroke instanceof com.googlecode.lanterna.input.MouseAction mouse) {
|
if (keyStroke instanceof com.googlecode.lanterna.input.MouseAction mouse) {
|
||||||
eventManager.emitEvent(new MouseAction(mouse));
|
eventManager.emitEvent(new MouseAction(mouse));
|
||||||
@@ -45,9 +52,12 @@ public class Cli implements Runnable {
|
|||||||
|
|
||||||
eventManager.emitEvent(new KeyboardPressEvent(keyStroke));
|
eventManager.emitEvent(new KeyboardPressEvent(keyStroke));
|
||||||
}
|
}
|
||||||
Thread.sleep(50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
terminal.close();
|
eventManager.emitEvent(RerenderScreen.full(terminal.getTerminalSize()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("Terminal error occurred, shutting down CLI thread.", e);
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/main/java/cz/jzitnik/config/EventThreadPoolConfig.java
Normal file
10
src/main/java/cz/jzitnik/config/EventThreadPoolConfig.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package cz.jzitnik.config;
|
||||||
|
|
||||||
|
import cz.jzitnik.annotations.Config;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Config
|
||||||
|
public class EventThreadPoolConfig {
|
||||||
|
private final int threadCount = 8;
|
||||||
|
}
|
||||||
28
src/main/java/cz/jzitnik/events/RerenderScreen.java
Normal file
28
src/main/java/cz/jzitnik/events/RerenderScreen.java
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package cz.jzitnik.events;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.TerminalSize;
|
||||||
|
import cz.jzitnik.utils.events.Event;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public record RerenderScreen(ScreenPart[] parts) implements Event {
|
||||||
|
public static RerenderScreen full(TerminalSize terminalSize) {
|
||||||
|
return new RerenderScreen(
|
||||||
|
new ScreenPart[]{
|
||||||
|
new ScreenPart(
|
||||||
|
new Point(0, 0),
|
||||||
|
new Point(terminalSize.getRows() - 1, terminalSize.getColumns() - 1)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class ScreenPart {
|
||||||
|
private Point start;
|
||||||
|
private Point end;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main/java/cz/jzitnik/events/TerminalResizeEvent.java
Normal file
12
src/main/java/cz/jzitnik/events/TerminalResizeEvent.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package cz.jzitnik.events;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.TerminalSize;
|
||||||
|
import cz.jzitnik.utils.events.Event;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public class TerminalResizeEvent implements Event {
|
||||||
|
private TerminalSize newSize;
|
||||||
|
}
|
||||||
54
src/main/java/cz/jzitnik/events/handlers/CliHandler.java
Normal file
54
src/main/java/cz/jzitnik/events/handlers/CliHandler.java
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package cz.jzitnik.events.handlers;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.TextCharacter;
|
||||||
|
import com.googlecode.lanterna.TextColor;
|
||||||
|
import com.googlecode.lanterna.screen.TerminalScreen;
|
||||||
|
import cz.jzitnik.annotations.EventHandler;
|
||||||
|
import cz.jzitnik.annotations.injectors.InjectState;
|
||||||
|
import cz.jzitnik.events.RerenderScreen;
|
||||||
|
import cz.jzitnik.states.ScreenBuffer;
|
||||||
|
import cz.jzitnik.ui.pixels.Empty;
|
||||||
|
import cz.jzitnik.ui.pixels.Pixel;
|
||||||
|
import cz.jzitnik.utils.DependencyManager;
|
||||||
|
import cz.jzitnik.utils.events.AbstractEventHandler;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
@EventHandler(RerenderScreen.class)
|
||||||
|
public class CliHandler extends AbstractEventHandler<RerenderScreen> {
|
||||||
|
@InjectState
|
||||||
|
private TerminalScreen terminalScreen;
|
||||||
|
|
||||||
|
@InjectState
|
||||||
|
private ScreenBuffer screenBuffer;
|
||||||
|
|
||||||
|
public CliHandler(DependencyManager dm) {
|
||||||
|
super(dm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(RerenderScreen event) {
|
||||||
|
var parts = event.parts();
|
||||||
|
var buffer = screenBuffer.getBuffer();
|
||||||
|
|
||||||
|
for (RerenderScreen.ScreenPart part : parts) {
|
||||||
|
Point a = part.getStart();
|
||||||
|
Point b = part.getEnd();
|
||||||
|
|
||||||
|
for (double y = a.getY(); y < b.getY(); y++) {
|
||||||
|
for (double x = a.getX(); x < b.getX(); x++) {
|
||||||
|
Pixel pixel = buffer[(int) y][(int) x];
|
||||||
|
terminalScreen.setCharacter(
|
||||||
|
(int) x,
|
||||||
|
(int) y,
|
||||||
|
new TextCharacter(
|
||||||
|
' ',
|
||||||
|
TextColor.ANSI.DEFAULT,
|
||||||
|
pixel.getClass().equals(Empty.class) ? TextColor.ANSI.DEFAULT : pixel.getColor()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,14 +24,9 @@ public class KeyboardPressEventHandler extends AbstractEventHandler<KeyboardPres
|
|||||||
case Escape:
|
case Escape:
|
||||||
eventManager.emitEvent(new ExitEvent());
|
eventManager.emitEvent(new ExitEvent());
|
||||||
break;
|
break;
|
||||||
case ArrowUp:
|
|
||||||
System.out.println("Up arrow pressed");
|
|
||||||
break;
|
|
||||||
case Character:
|
case Character:
|
||||||
System.out.println("Key pressed: " + keyStroke.getCharacter());
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.out.println("IDK: " + keyStroke.getKeyType());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,5 @@ public class MouseActionEventHandler extends AbstractEventHandler<MouseAction> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(MouseAction event) {
|
public void handle(MouseAction event) {
|
||||||
System.out.println("ACTION: " + event);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package cz.jzitnik.events.handlers;
|
||||||
|
|
||||||
|
import cz.jzitnik.annotations.EventHandler;
|
||||||
|
import cz.jzitnik.events.TerminalResizeEvent;
|
||||||
|
import cz.jzitnik.utils.DependencyManager;
|
||||||
|
import cz.jzitnik.utils.events.AbstractEventHandler;
|
||||||
|
|
||||||
|
@EventHandler(TerminalResizeEvent.class)
|
||||||
|
public class TerminalResizeEventHandler extends AbstractEventHandler<TerminalResizeEvent> {
|
||||||
|
public TerminalResizeEventHandler(DependencyManager dm) {
|
||||||
|
super(dm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(TerminalResizeEvent event) {
|
||||||
|
System.out.println("NEWSIZE: " + event.getNewSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
76
src/main/java/cz/jzitnik/sound/SoundPlayer.java
Normal file
76
src/main/java/cz/jzitnik/sound/SoundPlayer.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package cz.jzitnik.sound;
|
||||||
|
|
||||||
|
import cz.jzitnik.annotations.Dependency;
|
||||||
|
import cz.jzitnik.annotations.injectors.InjectDependency;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import javax.sound.sampled.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Dependency
|
||||||
|
public class SoundPlayer {
|
||||||
|
@InjectDependency
|
||||||
|
private ClassLoader classLoader;
|
||||||
|
|
||||||
|
public void playSound(String filePath, int backendVolume, int masterVolume)
|
||||||
|
throws LineUnavailableException, IOException, UnsupportedAudioFileException {
|
||||||
|
if (!filePath.endsWith(".ogg") || masterVolume == 0) {
|
||||||
|
return; // No sound if master volume is 0
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Loading resource: {}", "sounds/" + filePath);
|
||||||
|
var file = classLoader.getResourceAsStream("sounds/" + filePath);
|
||||||
|
if (file == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioInputStream audioStream = AudioSystem.getAudioInputStream(file);
|
||||||
|
AudioFormat baseFormat = audioStream.getFormat();
|
||||||
|
|
||||||
|
AudioFormat targetFormat = new AudioFormat(
|
||||||
|
AudioFormat.Encoding.PCM_SIGNED,
|
||||||
|
baseFormat.getSampleRate(),
|
||||||
|
16,
|
||||||
|
baseFormat.getChannels(),
|
||||||
|
baseFormat.getChannels() * 2,
|
||||||
|
baseFormat.getSampleRate(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
AudioInputStream dataIn = AudioSystem.getAudioInputStream(targetFormat, audioStream);
|
||||||
|
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
DataLine.Info info = new DataLine.Info(SourceDataLine.class, targetFormat);
|
||||||
|
|
||||||
|
try (SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info)) {
|
||||||
|
if (line != null) {
|
||||||
|
line.open(targetFormat);
|
||||||
|
line.start();
|
||||||
|
|
||||||
|
float finalVolume = (backendVolume / 100.0f) * (masterVolume / 100.0f);
|
||||||
|
log.info("Applying volume: {} (backend: {}, master: {})", finalVolume, backendVolume, masterVolume);
|
||||||
|
|
||||||
|
int bytesRead;
|
||||||
|
while ((bytesRead = dataIn.read(buffer, 0, buffer.length)) != -1) {
|
||||||
|
applyVolume(buffer, bytesRead, finalVolume);
|
||||||
|
line.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
|
||||||
|
line.drain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataIn.close();
|
||||||
|
audioStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyVolume(byte[] buffer, int bytesRead, float volume) {
|
||||||
|
for (int i = 0; i < bytesRead; i += 2) { // 16-bit PCM samples are 2 bytes each
|
||||||
|
int sample = (buffer[i] & 0xFF) | (buffer[i + 1] << 8);
|
||||||
|
sample = (int) (sample * volume);
|
||||||
|
buffer[i] = (byte) (sample & 0xFF);
|
||||||
|
buffer[i + 1] = (byte) ((sample >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/cz/jzitnik/states/ScreenBuffer.java
Normal file
11
src/main/java/cz/jzitnik/states/ScreenBuffer.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package cz.jzitnik.states;
|
||||||
|
|
||||||
|
import cz.jzitnik.annotations.State;
|
||||||
|
import cz.jzitnik.ui.pixels.Pixel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@State
|
||||||
|
public class ScreenBuffer {
|
||||||
|
private Pixel[][] buffer;
|
||||||
|
}
|
||||||
9
src/main/java/cz/jzitnik/ui/pixels/ColoredPixel.java
Normal file
9
src/main/java/cz/jzitnik/ui/pixels/ColoredPixel.java
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package cz.jzitnik.ui.pixels;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.TextColor;
|
||||||
|
|
||||||
|
public final class ColoredPixel extends Pixel {
|
||||||
|
public ColoredPixel(TextColor color) {
|
||||||
|
super(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/main/java/cz/jzitnik/ui/pixels/Empty.java
Normal file
7
src/main/java/cz/jzitnik/ui/pixels/Empty.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package cz.jzitnik.ui.pixels;
|
||||||
|
|
||||||
|
public final class Empty extends Pixel {
|
||||||
|
public Empty() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/cz/jzitnik/ui/pixels/Pixel.java
Normal file
11
src/main/java/cz/jzitnik/ui/pixels/Pixel.java
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package cz.jzitnik.ui.pixels;
|
||||||
|
|
||||||
|
import com.googlecode.lanterna.TextColor;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public sealed abstract class Pixel permits Empty, ColoredPixel {
|
||||||
|
protected TextColor color;
|
||||||
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package cz.jzitnik.utils;
|
package cz.jzitnik.utils;
|
||||||
|
|
||||||
|
// Don't blame me im using field injection instead of construction injection. I just like it more leave me alone.
|
||||||
|
// Yes I know I'll suffer in the unit tests.
|
||||||
|
|
||||||
import cz.jzitnik.annotations.Config;
|
import cz.jzitnik.annotations.Config;
|
||||||
import cz.jzitnik.annotations.Dependency;
|
import cz.jzitnik.annotations.Dependency;
|
||||||
import cz.jzitnik.annotations.injectors.InjectConfig;
|
import cz.jzitnik.annotations.injectors.InjectConfig;
|
||||||
@@ -47,10 +50,12 @@ public class DependencyManager {
|
|||||||
var instance = constructor.newInstance();
|
var instance = constructor.newInstance();
|
||||||
configs.put(configClass, instance);
|
configs.put(configClass, instance);
|
||||||
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
|
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
|
||||||
IllegalAccessException _) {
|
IllegalAccessException e) {
|
||||||
|
log.error("Failed to instantiate config class: {}", configClass.getName(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.put(ClassLoader.class, DependencyManager.class.getClassLoader());
|
||||||
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Dependency.class);
|
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(Dependency.class);
|
||||||
|
|
||||||
// Construct all classes
|
// Construct all classes
|
||||||
@@ -62,9 +67,7 @@ public class DependencyManager {
|
|||||||
|
|
||||||
for (int i = 0; i < paramTypes.length; i++) {
|
for (int i = 0; i < paramTypes.length; i++) {
|
||||||
Class<?> type = paramTypes[i];
|
Class<?> type = paramTypes[i];
|
||||||
if (configs.containsKey(type))
|
if (type == getClass())
|
||||||
params[i] = configs.get(type);
|
|
||||||
else if (type == getClass())
|
|
||||||
params[i] = this;
|
params[i] = this;
|
||||||
else if (type == Reflections.class)
|
else if (type == Reflections.class)
|
||||||
params[i] = reflections;
|
params[i] = reflections;
|
||||||
@@ -103,7 +106,7 @@ public class DependencyManager {
|
|||||||
|
|
||||||
if (!data.containsKey(field.getType())) continue;
|
if (!data.containsKey(field.getType())) continue;
|
||||||
|
|
||||||
Object dependency = data.get(field.getType());
|
Object dependency = field.getType() == getClass() ? this : data.get(field.getType());
|
||||||
|
|
||||||
if (!field.getType().isAssignableFrom(dependency.getClass())) continue;
|
if (!field.getType().isAssignableFrom(dependency.getClass())) continue;
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package cz.jzitnik.utils;
|
|||||||
|
|
||||||
import cz.jzitnik.annotations.Dependency;
|
import cz.jzitnik.annotations.Dependency;
|
||||||
import cz.jzitnik.annotations.State;
|
import cz.jzitnik.annotations.State;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Dependency
|
@Dependency
|
||||||
public class StateManager {
|
public class StateManager {
|
||||||
private final HashMap<Class<?>, Object> data = new HashMap<>();
|
private final HashMap<Class<?>, Object> data = new HashMap<>();
|
||||||
@@ -21,11 +23,16 @@ public class StateManager {
|
|||||||
var instance = clazz.getDeclaredConstructor().newInstance();
|
var instance = clazz.getDeclaredConstructor().newInstance();
|
||||||
data.put(clazz, instance);
|
data.put(clazz, instance);
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
||||||
NoSuchMethodException _) {
|
NoSuchMethodException e) {
|
||||||
|
log.error("Failed to instantiate state class: {}", clazz.getName(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerManually(Object instance) {
|
||||||
|
data.put(instance.getClass(), instance);
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> Optional<T> get(Class<T> clazz) {
|
public <T> Optional<T> get(Class<T> clazz) {
|
||||||
return Optional.ofNullable((T) data.get(clazz));
|
return Optional.ofNullable((T) data.get(clazz));
|
||||||
|
|||||||
@@ -2,21 +2,28 @@ package cz.jzitnik.utils.events;
|
|||||||
|
|
||||||
import cz.jzitnik.annotations.Dependency;
|
import cz.jzitnik.annotations.Dependency;
|
||||||
import cz.jzitnik.annotations.EventHandler;
|
import cz.jzitnik.annotations.EventHandler;
|
||||||
|
import cz.jzitnik.annotations.injectors.InjectConfig;
|
||||||
import cz.jzitnik.annotations.injectors.InjectState;
|
import cz.jzitnik.annotations.injectors.InjectState;
|
||||||
|
import cz.jzitnik.config.EventThreadPoolConfig;
|
||||||
import cz.jzitnik.states.RunningState;
|
import cz.jzitnik.states.RunningState;
|
||||||
import cz.jzitnik.utils.DependencyManager;
|
import cz.jzitnik.utils.DependencyManager;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
import org.w3c.dom.events.EventException;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Dependency
|
@Dependency
|
||||||
public class EventManager extends Thread {
|
public class EventManager extends Thread {
|
||||||
@InjectState
|
@InjectState
|
||||||
private RunningState runningState;
|
private RunningState runningState;
|
||||||
|
@InjectConfig
|
||||||
|
private EventThreadPoolConfig eventThreadPoolConfig;
|
||||||
|
|
||||||
|
private ExecutorService eventExecutor;
|
||||||
private final HashMap<Class<? extends Event>, AbstractEventHandler<? extends Event>> handlers = new HashMap<>();
|
private final HashMap<Class<? extends Event>, AbstractEventHandler<? extends Event>> handlers = new HashMap<>();
|
||||||
private final BlockingQueue<Event> eventQueue = new LinkedBlockingQueue<>();
|
private final BlockingQueue<Event> eventQueue = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
@@ -29,6 +36,7 @@ public class EventManager extends Thread {
|
|||||||
eventQueue.add(event);
|
eventQueue.add(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public EventManager(Reflections reflections, DependencyManager dependencyManager) {
|
public EventManager(Reflections reflections, DependencyManager dependencyManager) {
|
||||||
setDaemon(true);
|
setDaemon(true);
|
||||||
|
|
||||||
@@ -41,34 +49,34 @@ public class EventManager extends Thread {
|
|||||||
handlers.put(eventHandler.value(), instance);
|
handlers.put(eventHandler.value(), instance);
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
|
||||||
NoSuchMethodException e) {
|
NoSuchMethodException e) {
|
||||||
|
log.error("Failed to instantiate event handler: {}", clazz.getName(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(3000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
eventExecutor = Executors.newFixedThreadPool(eventThreadPoolConfig.getThreadCount());
|
||||||
while (runningState.isRunning()) {
|
while (runningState.isRunning()) {
|
||||||
try {
|
try {
|
||||||
Event event = eventQueue.take();
|
Event event = eventQueue.take();
|
||||||
handleEvent(event);
|
handleEvent(event);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
// Program stops
|
// The game is shutting down.
|
||||||
|
eventExecutor.shutdownNow();
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
eventExecutor.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <T extends Event> void handleEvent(Event event) {
|
private <T extends Event> void handleEvent(Event event) {
|
||||||
T typedEvent = (T) event; // safe because handler is keyed by event.getClass()
|
T typedEvent = (T) event;
|
||||||
AbstractEventHandler<T> handler = getHandler((Class<T>) event.getClass());
|
AbstractEventHandler<T> handler = getHandler((Class<T>) event.getClass());
|
||||||
Thread thread = new Thread(() -> handler.handle(typedEvent));
|
|
||||||
thread.setDaemon(true);
|
if (handler != null) {
|
||||||
thread.start();
|
eventExecutor.submit(() -> handler.handle(typedEvent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user