Added chat, improved player input responses, and rebalanced weapons.
This commit is contained in:
parent
bf0982fbd9
commit
e909b90457
|
@ -1,7 +1,13 @@
|
||||||
.idea/
|
.idea/
|
||||||
target/
|
target/
|
||||||
client-builds/
|
client-builds/
|
||||||
|
client.yaml
|
||||||
|
server.yaml
|
||||||
|
|
||||||
# Ignore the ./config directory so that developers can put their config files
|
# Ignore the ./config directory so that developers can put their config files
|
||||||
# there for server and client apps.
|
# there for server and client apps.
|
||||||
config
|
config
|
||||||
|
|
||||||
|
# Ignore compiled tool executables
|
||||||
|
build-clients
|
||||||
|
setversion
|
||||||
|
|
47
README.md
47
README.md
|
@ -8,45 +8,22 @@ _Read this guide to get started and join a server in just a minute or two!_
|
||||||
|
|
||||||
1. Make sure you've got at least Java 17 installed. You can get it [here](https://adoptium.net/temurin/releases).
|
1. Make sure you've got at least Java 17 installed. You can get it [here](https://adoptium.net/temurin/releases).
|
||||||
2. Download the `aos2-client` JAR file from the [releases page](https://github.com/andrewlalis/ace-of-shades-2/releases) that's compatible with your system.
|
2. Download the `aos2-client` JAR file from the [releases page](https://github.com/andrewlalis/ace-of-shades-2/releases) that's compatible with your system.
|
||||||
3. Run the game by double-clicking the JAR file, or entering `java -jar <jarfile>` in a terminal. This should generate a `config.yaml` file.
|
3. Run the game by double-clicking the JAR file, or entering `java -jar <jarfile>` in a terminal. This should generate a `client.yaml` file.
|
||||||
4. Set the `serverHost`, `serverPort`, and `username` properties accordingly for the server you want to join.
|
4. Set the `serverHost`, `serverPort`, and `username` properties accordingly for the server you want to join.
|
||||||
5. Run the game again to join the server and start playing!
|
5. Run the game again to join the server and start playing!
|
||||||
|
|
||||||
|
### Controls
|
||||||
|
- `WASD` to move, `SPACE` to jump, `LEFT-CONTROL` to crouch.
|
||||||
|
- `LEFT-CLICK` to use your held item (shoot, destroy blocks, etc.).
|
||||||
|
- `RIGHT-CLICK` to interact with things using your held item (place blocks, etc.).
|
||||||
|
- `R` to reload.
|
||||||
|
- `T` to chat. Press `ENTER` to send the chat.
|
||||||
|
- `ESCAPE` to exit the game.
|
||||||
|
- Numbers are used for selecting different items.
|
||||||
|
- `F3` toggles showing debug info.
|
||||||
|
|
||||||
## Setting up a Server
|
## Setting up a Server
|
||||||
Setting up a server is quite easy. Just go to the [releases page](https://github.com/andrewlalis/ace-of-shades-2/releases) and download the latest `aos2-server` JAR file. Similar to the client, it's best if you provide a `config.yaml` file to the server, in the same directory. The following snippet shows the structure and default values of a server's configuration.
|
Setting up a server is quite easy. Just go to the [releases page](https://github.com/andrewlalis/ace-of-shades-2/releases) and download the latest `aos2-server` JAR file, and run it. It'll create a `server.yaml` configuration file if you don't provide one.
|
||||||
```yaml
|
|
||||||
port: 25565
|
|
||||||
connectionBacklog: 5
|
|
||||||
ticksPerSecond: 20.0
|
|
||||||
world: worlds.redfort
|
|
||||||
teams:
|
|
||||||
- name: Red
|
|
||||||
color: [0.8, 0, 0]
|
|
||||||
spawnPoint: A
|
|
||||||
- name: Blue
|
|
||||||
color: [0, 0, 0.8]
|
|
||||||
spawnPoint: B
|
|
||||||
physics:
|
|
||||||
gravity: 29.43
|
|
||||||
walkingSpeed: 4
|
|
||||||
crouchingSpeed: 1.5
|
|
||||||
sprintingSpeed: 9
|
|
||||||
movementAcceleration: 2
|
|
||||||
movementDeceleration: 1
|
|
||||||
jumpVerticalSpeed: 8
|
|
||||||
actions:
|
|
||||||
blockBreakCooldown: 0.25
|
|
||||||
blockPlaceCooldown: 0.1
|
|
||||||
blockBreakReach: 5
|
|
||||||
blockPlaceReach: 5
|
|
||||||
blockBulletDamageResistance: 3
|
|
||||||
blockBulletDamageCooldown: 10
|
|
||||||
resupplyCooldown: 30
|
|
||||||
resupplyRadius: 3
|
|
||||||
teamSpawnProtection: 10
|
|
||||||
movementAccuracyDecreaseFactor: 0.01
|
|
||||||
friendlyFire: false
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
Both the client and server use a similar style of YAML-based configuration, where upon booting up, the program will look for a configuration file in the current working directory with one of the following names: `configuration`, `config`, `cfg`, ending in either `.yaml` or `.yml`. Alternatively, you can provide the path to a configuration file at a different location via a single command-line argument. For example:
|
Both the client and server use a similar style of YAML-based configuration, where upon booting up, the program will look for a configuration file in the current working directory with one of the following names: `configuration`, `config`, `cfg`, ending in either `.yaml` or `.yml`. Alternatively, you can provide the path to a configuration file at a different location via a single command-line argument. For example:
|
||||||
|
|
|
@ -83,14 +83,7 @@ public class Client implements Runnable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gameRenderer = new GameRenderer(this);
|
gameRenderer = new GameRenderer(this, inputHandler);
|
||||||
gameRenderer.setupWindow(
|
|
||||||
inputHandler,
|
|
||||||
new PlayerViewCursorCallback(config.input, this, gameRenderer.getCamera(), communicationHandler),
|
|
||||||
new PlayerInputKeyCallback(inputHandler),
|
|
||||||
new PlayerInputMouseClickCallback(inputHandler),
|
|
||||||
new PlayerInputMouseScrollCallback(this, communicationHandler)
|
|
||||||
);
|
|
||||||
soundManager = new SoundManager();
|
soundManager = new SoundManager();
|
||||||
log.debug("Sound system initialized.");
|
log.debug("Sound system initialized.");
|
||||||
|
|
||||||
|
@ -207,6 +200,13 @@ public class Client implements Runnable {
|
||||||
if (soundManager != null) {
|
if (soundManager != null) {
|
||||||
soundManager.play("chat", 1, myPlayer.getEyePosition(), myPlayer.getVelocity());
|
soundManager.play("chat", 1, myPlayer.getEyePosition(), myPlayer.getVelocity());
|
||||||
}
|
}
|
||||||
|
} else if (msg instanceof ClientOrientationUpdateMessage orientationUpdateMessage) {
|
||||||
|
runLater(() -> {
|
||||||
|
myPlayer.setOrientation(orientationUpdateMessage.x(), orientationUpdateMessage.y());
|
||||||
|
if (gameRenderer != null) {
|
||||||
|
gameRenderer.getCamera().setOrientationToPlayer(myPlayer);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,6 +222,10 @@ public class Client implements Runnable {
|
||||||
return inputHandler;
|
return inputHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CommunicationHandler getCommunicationHandler() {
|
||||||
|
return communicationHandler;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<Integer, Team> getTeams() {
|
public Map<Integer, Team> getTeams() {
|
||||||
return teams;
|
return teams;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import nl.andrewl.aos2_client.model.ClientPlayer;
|
||||||
import nl.andrewl.aos_core.model.item.BlockItemStack;
|
import nl.andrewl.aos_core.model.item.BlockItemStack;
|
||||||
import nl.andrewl.aos_core.model.world.Hit;
|
import nl.andrewl.aos_core.model.world.Hit;
|
||||||
import nl.andrewl.aos_core.net.client.BlockColorMessage;
|
import nl.andrewl.aos_core.net.client.BlockColorMessage;
|
||||||
|
import nl.andrewl.aos_core.net.client.ChatWrittenMessage;
|
||||||
import nl.andrewl.aos_core.net.client.ClientInputState;
|
import nl.andrewl.aos_core.net.client.ClientInputState;
|
||||||
|
|
||||||
import static org.lwjgl.glfw.GLFW.*;
|
import static org.lwjgl.glfw.GLFW.*;
|
||||||
|
@ -35,6 +36,9 @@ public class InputHandler {
|
||||||
|
|
||||||
private boolean debugEnabled;
|
private boolean debugEnabled;
|
||||||
|
|
||||||
|
private boolean chatting;
|
||||||
|
private StringBuffer chatText = new StringBuffer();
|
||||||
|
|
||||||
|
|
||||||
public InputHandler(Client client, CommunicationHandler comm) {
|
public InputHandler(Client client, CommunicationHandler comm) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
|
@ -66,6 +70,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setForward(boolean forward) {
|
public void setForward(boolean forward) {
|
||||||
|
if (chatting) return;
|
||||||
this.forward = forward;
|
this.forward = forward;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -75,6 +80,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBackward(boolean backward) {
|
public void setBackward(boolean backward) {
|
||||||
|
if (chatting) return;
|
||||||
this.backward = backward;
|
this.backward = backward;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -84,6 +90,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLeft(boolean left) {
|
public void setLeft(boolean left) {
|
||||||
|
if (chatting) return;
|
||||||
this.left = left;
|
this.left = left;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -93,6 +100,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRight(boolean right) {
|
public void setRight(boolean right) {
|
||||||
|
if (chatting) return;
|
||||||
this.right = right;
|
this.right = right;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -102,6 +110,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setJumping(boolean jumping) {
|
public void setJumping(boolean jumping) {
|
||||||
|
if (chatting) return;
|
||||||
this.jumping = jumping;
|
this.jumping = jumping;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -111,6 +120,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCrouching(boolean crouching) {
|
public void setCrouching(boolean crouching) {
|
||||||
|
if (chatting) return;
|
||||||
this.crouching = crouching;
|
this.crouching = crouching;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -120,6 +130,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSprinting(boolean sprinting) {
|
public void setSprinting(boolean sprinting) {
|
||||||
|
if (chatting) return;
|
||||||
this.sprinting = sprinting;
|
this.sprinting = sprinting;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -129,6 +140,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHitting(boolean hitting) {
|
public void setHitting(boolean hitting) {
|
||||||
|
if (chatting) return;
|
||||||
this.hitting = hitting;
|
this.hitting = hitting;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -138,6 +150,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setInteracting(boolean interacting) {
|
public void setInteracting(boolean interacting) {
|
||||||
|
if (chatting) return;
|
||||||
this.interacting = interacting;
|
this.interacting = interacting;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -147,6 +160,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReloading(boolean reloading) {
|
public void setReloading(boolean reloading) {
|
||||||
|
if (chatting) return;
|
||||||
this.reloading = reloading;
|
this.reloading = reloading;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -156,6 +170,7 @@ public class InputHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectedInventoryIndex(int selectedInventoryIndex) {
|
public void setSelectedInventoryIndex(int selectedInventoryIndex) {
|
||||||
|
if (chatting) return;
|
||||||
this.selectedInventoryIndex = selectedInventoryIndex;
|
this.selectedInventoryIndex = selectedInventoryIndex;
|
||||||
updateInputState();
|
updateInputState();
|
||||||
}
|
}
|
||||||
|
@ -168,6 +183,57 @@ public class InputHandler {
|
||||||
this.debugEnabled = !debugEnabled;
|
this.debugEnabled = !debugEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enableChatting() {
|
||||||
|
if (chatting) return;
|
||||||
|
setForward(false);
|
||||||
|
setBackward(false);
|
||||||
|
setLeft(false);
|
||||||
|
setRight(false);
|
||||||
|
setJumping(false);
|
||||||
|
setCrouching(false);
|
||||||
|
setSprinting(false);
|
||||||
|
setReloading(false);
|
||||||
|
chatting = true;
|
||||||
|
chatText = new StringBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChatting() {
|
||||||
|
return chatting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancelChatting() {
|
||||||
|
chatting = false;
|
||||||
|
chatText.delete(0, chatText.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendToChat(int codePoint) {
|
||||||
|
if (!chatting || chatText.length() + 1 > 120) return;
|
||||||
|
chatText.appendCodePoint(codePoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendToChat(String s) {
|
||||||
|
if (!chatting || chatText.length() + s.length() > 120) return;
|
||||||
|
chatText.append(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteFromChat() {
|
||||||
|
if (!chatting || chatText.length() == 0) return;
|
||||||
|
chatText.deleteCharAt(chatText.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChatText() {
|
||||||
|
return chatText.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendChat() {
|
||||||
|
if (!chatting) return;
|
||||||
|
String text = chatText.toString().trim();
|
||||||
|
cancelChatting();
|
||||||
|
if (!text.isBlank()) {
|
||||||
|
client.getCommunicationHandler().sendMessage(new ChatWrittenMessage(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void pickBlock() {
|
public void pickBlock() {
|
||||||
var player = client.getMyPlayer();
|
var player = client.getMyPlayer();
|
||||||
if (player.getInventory().getSelectedItemStack() instanceof BlockItemStack stack) {
|
if (player.getInventory().getSelectedItemStack() instanceof BlockItemStack stack) {
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package nl.andrewl.aos2_client.control;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFWCharCallbackI;
|
||||||
|
|
||||||
|
public class PlayerCharacterInputCallback implements GLFWCharCallbackI {
|
||||||
|
private final InputHandler inputHandler;
|
||||||
|
|
||||||
|
public PlayerCharacterInputCallback(InputHandler inputHandler) {
|
||||||
|
this.inputHandler = inputHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(long window, int codepoint) {
|
||||||
|
if (inputHandler.isChatting()) {
|
||||||
|
inputHandler.appendToChat(codepoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,12 +24,23 @@ public class PlayerInputKeyCallback implements GLFWKeyCallbackI {
|
||||||
case GLFW_KEY_LEFT_SHIFT -> inputHandler.setSprinting(true);
|
case GLFW_KEY_LEFT_SHIFT -> inputHandler.setSprinting(true);
|
||||||
case GLFW_KEY_R -> inputHandler.setReloading(true);
|
case GLFW_KEY_R -> inputHandler.setReloading(true);
|
||||||
|
|
||||||
|
case GLFW_KEY_BACKSPACE -> inputHandler.deleteFromChat();
|
||||||
|
case GLFW_KEY_ENTER -> inputHandler.sendChat();
|
||||||
|
|
||||||
case GLFW_KEY_1 -> inputHandler.setSelectedInventoryIndex(0);
|
case GLFW_KEY_1 -> inputHandler.setSelectedInventoryIndex(0);
|
||||||
case GLFW_KEY_2 -> inputHandler.setSelectedInventoryIndex(1);
|
case GLFW_KEY_2 -> inputHandler.setSelectedInventoryIndex(1);
|
||||||
case GLFW_KEY_3 -> inputHandler.setSelectedInventoryIndex(2);
|
case GLFW_KEY_3 -> inputHandler.setSelectedInventoryIndex(2);
|
||||||
case GLFW_KEY_4 -> inputHandler.setSelectedInventoryIndex(3);
|
case GLFW_KEY_4 -> inputHandler.setSelectedInventoryIndex(3);
|
||||||
|
|
||||||
case GLFW_KEY_F3 -> inputHandler.toggleDebugEnabled();
|
case GLFW_KEY_F3 -> inputHandler.toggleDebugEnabled();
|
||||||
|
|
||||||
|
case GLFW_KEY_ESCAPE -> {
|
||||||
|
if (inputHandler.isChatting()) {
|
||||||
|
inputHandler.cancelChatting();
|
||||||
|
} else {
|
||||||
|
glfwSetWindowShouldClose(window, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (action == GLFW_RELEASE) {
|
} else if (action == GLFW_RELEASE) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
@ -42,7 +53,15 @@ public class PlayerInputKeyCallback implements GLFWKeyCallbackI {
|
||||||
case GLFW_KEY_LEFT_SHIFT -> inputHandler.setSprinting(false);
|
case GLFW_KEY_LEFT_SHIFT -> inputHandler.setSprinting(false);
|
||||||
case GLFW_KEY_R -> inputHandler.setReloading(false);
|
case GLFW_KEY_R -> inputHandler.setReloading(false);
|
||||||
|
|
||||||
case GLFW_KEY_ESCAPE -> glfwSetWindowShouldClose(window, true);
|
case GLFW_KEY_T -> inputHandler.enableChatting();
|
||||||
|
case GLFW_KEY_SLASH -> {
|
||||||
|
inputHandler.enableChatting();
|
||||||
|
inputHandler.appendToChat("/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (action == GLFW_REPEAT) {
|
||||||
|
switch (key) {
|
||||||
|
case GLFW_KEY_BACKSPACE -> inputHandler.deleteFromChat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,9 @@ public class PlayerInputMouseScrollCallback implements GLFWScrollCallbackI {
|
||||||
private final Client client;
|
private final Client client;
|
||||||
private final CommunicationHandler comm;
|
private final CommunicationHandler comm;
|
||||||
|
|
||||||
public PlayerInputMouseScrollCallback(Client client, CommunicationHandler comm) {
|
public PlayerInputMouseScrollCallback(Client client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.comm = comm;
|
this.comm = client.getCommunicationHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -30,11 +30,11 @@ public class PlayerViewCursorCallback implements GLFWCursorPosCallbackI {
|
||||||
private float lastMouseCursorY;
|
private float lastMouseCursorY;
|
||||||
private long lastOrientationUpdateSentAt = 0L;
|
private long lastOrientationUpdateSentAt = 0L;
|
||||||
|
|
||||||
public PlayerViewCursorCallback(ClientConfig.InputConfig config, Client client, Camera cam, CommunicationHandler comm) {
|
public PlayerViewCursorCallback(Client client, Camera cam) {
|
||||||
this.config = config;
|
this.config = client.getConfig().input;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.camera = cam;
|
this.camera = cam;
|
||||||
this.comm = comm;
|
this.comm = client.getCommunicationHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,7 +3,7 @@ package nl.andrewl.aos2_client.render;
|
||||||
import nl.andrewl.aos2_client.Camera;
|
import nl.andrewl.aos2_client.Camera;
|
||||||
import nl.andrewl.aos2_client.Client;
|
import nl.andrewl.aos2_client.Client;
|
||||||
import nl.andrewl.aos2_client.config.ClientConfig;
|
import nl.andrewl.aos2_client.config.ClientConfig;
|
||||||
import nl.andrewl.aos2_client.control.InputHandler;
|
import nl.andrewl.aos2_client.control.*;
|
||||||
import nl.andrewl.aos2_client.model.ClientPlayer;
|
import nl.andrewl.aos2_client.model.ClientPlayer;
|
||||||
import nl.andrewl.aos2_client.render.chunk.ChunkRenderer;
|
import nl.andrewl.aos2_client.render.chunk.ChunkRenderer;
|
||||||
import nl.andrewl.aos2_client.render.gui.GuiRenderer;
|
import nl.andrewl.aos2_client.render.gui.GuiRenderer;
|
||||||
|
@ -36,42 +36,36 @@ public class GameRenderer {
|
||||||
private static final float Z_FAR = 500f;
|
private static final float Z_FAR = 500f;
|
||||||
|
|
||||||
private final ClientConfig.DisplayConfig config;
|
private final ClientConfig.DisplayConfig config;
|
||||||
private ChunkRenderer chunkRenderer;
|
private final ChunkRenderer chunkRenderer;
|
||||||
private GuiRenderer guiRenderer;
|
private final GuiRenderer guiRenderer;
|
||||||
private ModelRenderer modelRenderer;
|
private final ModelRenderer modelRenderer;
|
||||||
private final Camera camera;
|
private final Camera camera;
|
||||||
private final Client client;
|
private final Client client;
|
||||||
|
|
||||||
// Standard models for various game components.
|
// Standard models for various game components.
|
||||||
private Model playerModel;
|
private final Model playerModel;
|
||||||
private Model rifleModel;
|
private final Model rifleModel;
|
||||||
private Model blockModel;
|
private final Model blockModel;
|
||||||
private Model bulletModel;
|
private final Model bulletModel;
|
||||||
private Model smgModel;
|
private final Model smgModel;
|
||||||
private Model shotgunModel;
|
private final Model shotgunModel;
|
||||||
private Model flagModel;
|
private final Model flagModel;
|
||||||
|
|
||||||
private long windowHandle;
|
private final long windowHandle;
|
||||||
private int screenWidth = 800;
|
private final int screenWidth;
|
||||||
private int screenHeight = 600;
|
private final int screenHeight;
|
||||||
|
|
||||||
private final Matrix4f perspectiveTransform;
|
private final Matrix4f perspectiveTransform;
|
||||||
|
|
||||||
public GameRenderer(Client client) {
|
public GameRenderer(Client client, InputHandler inputHandler) {
|
||||||
this.config = client.getConfig().display;
|
this.config = client.getConfig().display;
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.camera = new Camera();
|
this.camera = new Camera();
|
||||||
camera.setToPlayer(client.getMyPlayer());
|
camera.setToPlayer(client.getMyPlayer());
|
||||||
this.perspectiveTransform = new Matrix4f();
|
this.perspectiveTransform = new Matrix4f();
|
||||||
}
|
|
||||||
|
|
||||||
public void setupWindow(
|
// Initialize window!
|
||||||
InputHandler inputHandler,
|
|
||||||
GLFWCursorPosCallbackI viewCursorCallback,
|
|
||||||
GLFWKeyCallbackI inputKeyCallback,
|
|
||||||
GLFWMouseButtonCallbackI mouseButtonCallback,
|
|
||||||
GLFWScrollCallbackI scrollCallback
|
|
||||||
) {
|
|
||||||
GLFWErrorCallback.createPrint(System.err).set();
|
GLFWErrorCallback.createPrint(System.err).set();
|
||||||
if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW.");
|
if (!glfwInit()) throw new IllegalStateException("Could not initialize GLFW.");
|
||||||
glfwDefaultWindowHints();
|
glfwDefaultWindowHints();
|
||||||
|
@ -96,10 +90,11 @@ public class GameRenderer {
|
||||||
log.debug("Initialized GLFW window.");
|
log.debug("Initialized GLFW window.");
|
||||||
|
|
||||||
// Setup callbacks.
|
// Setup callbacks.
|
||||||
glfwSetKeyCallback(windowHandle, inputKeyCallback);
|
glfwSetKeyCallback(windowHandle, new PlayerInputKeyCallback(inputHandler));
|
||||||
glfwSetCursorPosCallback(windowHandle, viewCursorCallback);
|
glfwSetCursorPosCallback(windowHandle, new PlayerViewCursorCallback(client, camera));
|
||||||
glfwSetMouseButtonCallback(windowHandle, mouseButtonCallback);
|
glfwSetMouseButtonCallback(windowHandle, new PlayerInputMouseClickCallback(inputHandler));
|
||||||
glfwSetScrollCallback(windowHandle, scrollCallback);
|
glfwSetScrollCallback(windowHandle, new PlayerInputMouseScrollCallback(client));
|
||||||
|
glfwSetCharCallback(windowHandle, new PlayerCharacterInputCallback(inputHandler));
|
||||||
if (config.captureCursor) {
|
if (config.captureCursor) {
|
||||||
glfwSetInputMode(windowHandle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
glfwSetInputMode(windowHandle, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||||
}
|
}
|
||||||
|
@ -282,15 +277,15 @@ public class GameRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void freeWindow() {
|
public void freeWindow() {
|
||||||
if (rifleModel != null) rifleModel.free();
|
rifleModel.free();
|
||||||
if (smgModel != null) smgModel.free();
|
smgModel.free();
|
||||||
if (flagModel != null) flagModel.free();
|
flagModel.free();
|
||||||
if (bulletModel != null) bulletModel.free();
|
bulletModel.free();
|
||||||
if (playerModel != null) playerModel.free();
|
playerModel.free();
|
||||||
if (blockModel != null) blockModel.free();
|
blockModel.free();
|
||||||
if (modelRenderer != null) modelRenderer.free();
|
modelRenderer.free();
|
||||||
if (guiRenderer != null) guiRenderer.free();
|
guiRenderer.free();
|
||||||
if (chunkRenderer != null) chunkRenderer.free();
|
chunkRenderer.free();
|
||||||
GL.destroy();
|
GL.destroy();
|
||||||
Callbacks.glfwFreeCallbacks(windowHandle);
|
Callbacks.glfwFreeCallbacks(windowHandle);
|
||||||
glfwSetErrorCallback(null);
|
glfwSetErrorCallback(null);
|
||||||
|
|
|
@ -223,7 +223,7 @@ public class GuiRenderer {
|
||||||
nvgSave(vgId);
|
nvgSave(vgId);
|
||||||
|
|
||||||
drawCrosshair(width, height);
|
drawCrosshair(width, height);
|
||||||
drawChat(width, height, client.getChat());
|
drawChat(width, height, client);
|
||||||
drawHealthBar(width, height, client.getMyPlayer());
|
drawHealthBar(width, height, client.getMyPlayer());
|
||||||
drawHeldItemStackInfo(width, height, client.getMyPlayer());
|
drawHeldItemStackInfo(width, height, client.getMyPlayer());
|
||||||
if (client.getInputHandler().isDebugEnabled()) {
|
if (client.getInputHandler().isDebugEnabled()) {
|
||||||
|
@ -269,18 +269,18 @@ public class GuiRenderer {
|
||||||
private void drawHealthBar(float w, float h, ClientPlayer player) {
|
private void drawHealthBar(float w, float h, ClientPlayer player) {
|
||||||
nvgFillColor(vgId, GuiUtils.rgba(1, 0, 0, 1, colorA));
|
nvgFillColor(vgId, GuiUtils.rgba(1, 0, 0, 1, colorA));
|
||||||
nvgBeginPath(vgId);
|
nvgBeginPath(vgId);
|
||||||
nvgRect(vgId, 20, h - 60, 100, 20);
|
nvgRect(vgId, w - 170, h - 110, 100, 20);
|
||||||
nvgFill(vgId);
|
nvgFill(vgId);
|
||||||
nvgFillColor(vgId, GuiUtils.rgba(0, 1, 0, 1, colorA));
|
nvgFillColor(vgId, GuiUtils.rgba(0, 1, 0, 1, colorA));
|
||||||
nvgBeginPath(vgId);
|
nvgBeginPath(vgId);
|
||||||
nvgRect(vgId, 20, h - 60, 100 * player.getHealth(), 20);
|
nvgRect(vgId, w - 170, h - 110, 100 * player.getHealth(), 20);
|
||||||
nvgFill(vgId);
|
nvgFill(vgId);
|
||||||
|
|
||||||
nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 1, colorA));
|
nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 1, colorA));
|
||||||
nvgFontSize(vgId, 12f);
|
nvgFontSize(vgId, 12f);
|
||||||
nvgFontFaceId(vgId, jetbrainsMonoFont);
|
nvgFontFaceId(vgId, jetbrainsMonoFont);
|
||||||
nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
||||||
nvgText(vgId, 20, h - 30, String.format("%.2f / 1.00 HP", player.getHealth()));
|
nvgText(vgId, w - 170, h - 80, String.format("%.2f / 1.00 HP", player.getHealth()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawHeldItemStackInfo(float w, float h, ClientPlayer player) {
|
private void drawHeldItemStackInfo(float w, float h, ClientPlayer player) {
|
||||||
|
@ -324,19 +324,20 @@ public class GuiRenderer {
|
||||||
nvgText(vgId, w - 140, h - 14, String.format("Selected value: %d", stack.getSelectedValue()));
|
nvgText(vgId, w - 140, h - 14, String.format("Selected value: %d", stack.getSelectedValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawChat(float w, float h, Chat chat) {
|
private void drawChat(float w, float h, Client client) {
|
||||||
float chatWidth = w / 3;
|
float chatWidth = w / 3;
|
||||||
float chatHeight = h / 4;
|
float chatHeight = h / 4;
|
||||||
|
|
||||||
nvgFillColor(vgId, GuiUtils.rgba(0, 0, 0, 0.25f, colorA));
|
nvgFillColor(vgId, GuiUtils.rgba(0, 0, 0, 0.25f, colorA));
|
||||||
nvgBeginPath(vgId);
|
nvgBeginPath(vgId);
|
||||||
nvgRect(vgId, 0, 0, chatWidth, chatHeight);
|
nvgRect(vgId, 0, h - chatHeight - 16, chatWidth, chatHeight);
|
||||||
nvgFill(vgId);
|
nvgFill(vgId);
|
||||||
|
|
||||||
|
var chat = client.getChat();
|
||||||
nvgFontSize(vgId, 12f);
|
nvgFontSize(vgId, 12f);
|
||||||
nvgFontFaceId(vgId, jetbrainsMonoFont);
|
nvgFontFaceId(vgId, jetbrainsMonoFont);
|
||||||
nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
|
||||||
float y = chatHeight - 12;
|
float y = h - 16 - 12;
|
||||||
for (var msg : chat.getMessages()) {
|
for (var msg : chat.getMessages()) {
|
||||||
if (msg.author().equals("_ANNOUNCE")) {
|
if (msg.author().equals("_ANNOUNCE")) {
|
||||||
nvgFillColor(vgId, GuiUtils.rgba(0.7f, 0, 0, 1, colorA));
|
nvgFillColor(vgId, GuiUtils.rgba(0.7f, 0, 0, 1, colorA));
|
||||||
|
@ -348,9 +349,17 @@ public class GuiRenderer {
|
||||||
nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 1, colorA));
|
nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 1, colorA));
|
||||||
nvgText(vgId, 5, y, msg.author() + ": " + msg.message());
|
nvgText(vgId, 5, y, msg.author() + ": " + msg.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
y -= 16;
|
y -= 16;
|
||||||
}
|
}
|
||||||
|
var input = client.getInputHandler();
|
||||||
|
if (input.isChatting()) {
|
||||||
|
nvgFillColor(vgId, GuiUtils.rgba(0, 0, 0, 0.5f, colorA));
|
||||||
|
nvgBeginPath(vgId);
|
||||||
|
nvgRect(vgId, 0, h - 16, w, 16);
|
||||||
|
nvgFill(vgId);
|
||||||
|
nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 1, colorA));
|
||||||
|
nvgText(vgId, 5, h - 14, "> " + input.getChatText() + "_");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawDebugInfo(float w, float h, Client client) {
|
private void drawDebugInfo(float w, float h, Client client) {
|
||||||
|
|
|
@ -48,6 +48,7 @@ public final class Net {
|
||||||
serializer.registerType(19, BlockColorMessage.class);
|
serializer.registerType(19, BlockColorMessage.class);
|
||||||
serializer.registerType(20, ChatMessage.class);
|
serializer.registerType(20, ChatMessage.class);
|
||||||
serializer.registerType(21, ChatWrittenMessage.class);
|
serializer.registerType(21, ChatWrittenMessage.class);
|
||||||
|
serializer.registerType(22, ClientOrientationUpdateMessage.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExtendedDataInputStream getInputStream(InputStream in) {
|
public static ExtendedDataInputStream getInputStream(InputStream in) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ public class Ak47 extends Gun {
|
||||||
0.1f,
|
0.1f,
|
||||||
1.2f,
|
1.2f,
|
||||||
0.4f,
|
0.4f,
|
||||||
30f,
|
0.1f,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,14 @@ public class Rifle extends Gun {
|
||||||
super(
|
super(
|
||||||
id,
|
id,
|
||||||
"Rifle",
|
"Rifle",
|
||||||
5,
|
6,
|
||||||
8,
|
8,
|
||||||
1,
|
1,
|
||||||
0.97f,
|
0.98f,
|
||||||
0.8f,
|
0.8f,
|
||||||
2.5f,
|
2.5f,
|
||||||
0.8f,
|
0.8f,
|
||||||
50f,
|
0.2f,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,14 @@ public class Winchester extends Gun {
|
||||||
super(
|
super(
|
||||||
id,
|
id,
|
||||||
"Winchester",
|
"Winchester",
|
||||||
10,
|
|
||||||
6,
|
6,
|
||||||
4,
|
4,
|
||||||
|
4,
|
||||||
0.85f,
|
0.85f,
|
||||||
0.75f,
|
0.75f,
|
||||||
2.5f,
|
2.5f,
|
||||||
0.3f,
|
0.3f,
|
||||||
60f,
|
0.33f,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,6 @@ appender.console.name = STDOUT
|
||||||
appender.console.layout.type = PatternLayout
|
appender.console.layout.type = PatternLayout
|
||||||
appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
|
appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n
|
||||||
|
|
||||||
rootLogger.level = debug
|
rootLogger.level = info
|
||||||
rootLogger.appenderRefs = stdout
|
rootLogger.appenderRefs = stdout
|
||||||
rootLogger.appenderRef.stdout.ref = STDOUT
|
rootLogger.appenderRef.stdout.ref = STDOUT
|
|
@ -7,6 +7,8 @@ import nl.andrewl.aos_core.model.item.ItemStack;
|
||||||
import nl.andrewl.aos_core.model.world.Chunk;
|
import nl.andrewl.aos_core.model.world.Chunk;
|
||||||
import nl.andrewl.aos_core.model.world.WorldIO;
|
import nl.andrewl.aos_core.model.world.WorldIO;
|
||||||
import nl.andrewl.aos_core.net.TcpReceiver;
|
import nl.andrewl.aos_core.net.TcpReceiver;
|
||||||
|
import nl.andrewl.aos_core.net.client.ChatMessage;
|
||||||
|
import nl.andrewl.aos_core.net.client.ChatWrittenMessage;
|
||||||
import nl.andrewl.aos_core.net.connect.ConnectAcceptMessage;
|
import nl.andrewl.aos_core.net.connect.ConnectAcceptMessage;
|
||||||
import nl.andrewl.aos_core.net.connect.ConnectRejectMessage;
|
import nl.andrewl.aos_core.net.connect.ConnectRejectMessage;
|
||||||
import nl.andrewl.aos_core.net.connect.ConnectRequestMessage;
|
import nl.andrewl.aos_core.net.connect.ConnectRequestMessage;
|
||||||
|
@ -77,6 +79,25 @@ public class ClientCommunicationHandler {
|
||||||
if (chunk != null && hashMessage.hash() != chunk.blockHash()) {
|
if (chunk != null && hashMessage.hash() != chunk.blockHash()) {
|
||||||
sendTcpMessage(new ChunkDataMessage(chunk));
|
sendTcpMessage(new ChunkDataMessage(chunk));
|
||||||
}
|
}
|
||||||
|
} else if (msg instanceof ChatWrittenMessage chatWrittenMessage) {
|
||||||
|
if (chatWrittenMessage.message().startsWith("/t ")) {
|
||||||
|
if (player.getTeam() != null) {
|
||||||
|
var chat = new ChatMessage(
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
player.getUsername(),
|
||||||
|
chatWrittenMessage.message().substring(3)
|
||||||
|
);
|
||||||
|
for (var teamPlayer : server.getTeamManager().getPlayers(player.getTeam())) {
|
||||||
|
server.getPlayerManager().getHandler(teamPlayer).sendTcpMessage(chat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
server.getPlayerManager().broadcastTcpMessage(new ChatMessage(
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
player.getUsername(),
|
||||||
|
chatWrittenMessage.message()
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +131,10 @@ public class ClientCommunicationHandler {
|
||||||
log.debug("Sent connect accept message.");
|
log.debug("Sent connect accept message.");
|
||||||
sendInitialData();
|
sendInitialData();
|
||||||
log.debug("Sent initial data.");
|
log.debug("Sent initial data.");
|
||||||
|
sendTcpMessage(ChatMessage.privateMessage("Welcome to the server, " + player.getUsername() + "."));
|
||||||
|
if (player.getTeam() != null) {
|
||||||
|
sendTcpMessage(ChatMessage.privateMessage("You've joined the " + player.getTeam().getName() + " team."));
|
||||||
|
}
|
||||||
// Initiate a TCP receiver thread to accept incoming messages from the client.
|
// Initiate a TCP receiver thread to accept incoming messages from the client.
|
||||||
TcpReceiver tcpReceiver = new TcpReceiver(in, this::handleTcpMessage)
|
TcpReceiver tcpReceiver = new TcpReceiver(in, this::handleTcpMessage)
|
||||||
.withShutdownHook(() -> server.getPlayerManager().deregister(this.player));
|
.withShutdownHook(() -> server.getPlayerManager().deregister(this.player));
|
||||||
|
@ -135,11 +160,13 @@ public class ClientCommunicationHandler {
|
||||||
|
|
||||||
public void sendTcpMessage(Message msg) {
|
public void sendTcpMessage(Message msg) {
|
||||||
ForkJoinPool.commonPool().submit(() -> {
|
ForkJoinPool.commonPool().submit(() -> {
|
||||||
|
synchronized (out) {
|
||||||
try {
|
try {
|
||||||
Net.write(msg, out);
|
Net.write(msg, out);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -111,6 +111,16 @@ public class PlayerActionManager {
|
||||||
gunNeedsReCock = true;
|
gunNeedsReCock = true;
|
||||||
}
|
}
|
||||||
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory()));
|
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ItemStackMessage(player.getInventory()));
|
||||||
|
// Apply recoil!
|
||||||
|
float recoilFactor = 10f; // Maximum number of degrees to recoil.
|
||||||
|
float recoil = recoilFactor * gun.getRecoil() + (float) ThreadLocalRandom.current().nextGaussian(0, 0.01);
|
||||||
|
player.getOrientation().y += Math.toRadians(recoil);
|
||||||
|
server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new ClientOrientationUpdateMessage(
|
||||||
|
player.getOrientation().x(),
|
||||||
|
player.getOrientation().y()
|
||||||
|
));
|
||||||
|
server.getPlayerManager().broadcastUdpMessageToAllBut(player.getUpdateMessage(now), player);
|
||||||
|
// Play sound!
|
||||||
String shotSound = null;
|
String shotSound = null;
|
||||||
if (gun instanceof Rifle) {
|
if (gun instanceof Rifle) {
|
||||||
shotSound = "shot_m1-garand_1";
|
shotSound = "shot_m1-garand_1";
|
||||||
|
|
|
@ -2,6 +2,11 @@ package nl.andrewl.aos2_server.logic;
|
||||||
|
|
||||||
import nl.andrewl.aos_core.net.client.ClientInputState;
|
import nl.andrewl.aos_core.net.client.ClientInputState;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that keeps track of any impulses the player has made since the
|
||||||
|
* last tick. This way, if a player activates an input for even a fraction of
|
||||||
|
* a tick, it'll register.
|
||||||
|
*/
|
||||||
public class PlayerImpulses {
|
public class PlayerImpulses {
|
||||||
public boolean forward;
|
public boolean forward;
|
||||||
public boolean backward;
|
public boolean backward;
|
||||||
|
|
Loading…
Reference in New Issue