Added bullets!
|
@ -12,6 +12,7 @@ import nl.andrewl.aos2_client.sound.SoundManager;
|
|||
import nl.andrewl.aos2_client.sound.SoundSource;
|
||||
import nl.andrewl.aos_core.config.Config;
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
import nl.andrewl.aos_core.model.Projectile;
|
||||
import nl.andrewl.aos_core.model.Team;
|
||||
import nl.andrewl.aos_core.net.client.*;
|
||||
import nl.andrewl.aos_core.net.world.ChunkDataMessage;
|
||||
|
@ -43,12 +44,14 @@ public class Client implements Runnable {
|
|||
private ClientWorld world;
|
||||
private ClientPlayer myPlayer;
|
||||
private final Map<Integer, OtherPlayer> players;
|
||||
private final Map<Integer, Projectile> projectiles;
|
||||
private Map<Integer, Team> teams;
|
||||
|
||||
public Client(ClientConfig config) {
|
||||
this.config = config;
|
||||
this.players = new ConcurrentHashMap<>();
|
||||
this.teams = new ConcurrentHashMap<>();
|
||||
this.projectiles = new ConcurrentHashMap<>();
|
||||
this.communicationHandler = new CommunicationHandler(this);
|
||||
this.inputHandler = new InputHandler(this, communicationHandler);
|
||||
}
|
||||
|
@ -96,6 +99,7 @@ public class Client implements Runnable {
|
|||
world.processQueuedChunkUpdates();
|
||||
gameRenderer.getCamera().interpolatePosition(dt);
|
||||
interpolatePlayers(dt);
|
||||
interpolateProjectiles(dt);
|
||||
gameRenderer.draw();
|
||||
lastFrameAt = now;
|
||||
}
|
||||
|
@ -120,7 +124,9 @@ public class Client implements Runnable {
|
|||
if (gameRenderer != null) {
|
||||
gameRenderer.getCamera().setToPlayer(myPlayer);
|
||||
}
|
||||
soundManager.updateListener(myPlayer.getPosition(), myPlayer.getVelocity());
|
||||
if (soundManager != null) {
|
||||
soundManager.updateListener(myPlayer.getPosition(), myPlayer.getVelocity());
|
||||
}
|
||||
lastPlayerUpdate = playerUpdate.timestamp();
|
||||
} else {
|
||||
OtherPlayer p = players.get(playerUpdate.clientId());
|
||||
|
@ -151,6 +157,18 @@ public class Client implements Runnable {
|
|||
players.remove(leaveMessage.id());
|
||||
} else if (msg instanceof SoundMessage soundMessage) {
|
||||
playerSource.play(soundManager.getSoundBuffer(soundMessage.name()));
|
||||
} else if (msg instanceof ProjectileMessage pm) {
|
||||
Projectile p = projectiles.get(pm.id());
|
||||
if (p == null && !pm.destroyed()) {
|
||||
p = new Projectile(pm.id(), new Vector3f(pm.px(), pm.py(), pm.pz()), new Vector3f(pm.vx(), pm.vy(), pm.vz()), pm.type());
|
||||
projectiles.put(p.getId(), p);
|
||||
} else if (p != null) {
|
||||
p.getPosition().set(pm.px(), pm.py(), pm.pz()); // Don't update position, it's too short of a timeframe to matter.
|
||||
p.getVelocity().set(pm.vx(), pm.vy(), pm.vz());
|
||||
if (pm.destroyed()) {
|
||||
projectiles.remove(p.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,6 +192,10 @@ public class Client implements Runnable {
|
|||
return players;
|
||||
}
|
||||
|
||||
public Map<Integer, Projectile> getProjectiles() {
|
||||
return projectiles;
|
||||
}
|
||||
|
||||
public void interpolatePlayers(float dt) {
|
||||
Vector3f movement = new Vector3f();
|
||||
for (var player : players.values()) {
|
||||
|
@ -183,6 +205,14 @@ public class Client implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
public void interpolateProjectiles(float dt) {
|
||||
Vector3f movement = new Vector3f();
|
||||
for (var proj : projectiles.values()) {
|
||||
movement.set(proj.getVelocity()).mul(dt);
|
||||
proj.getPosition().add(movement);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
List<Path> configPaths = Config.getCommonConfigPaths();
|
||||
if (args.length > 0) {
|
||||
|
|
|
@ -12,8 +12,8 @@ public class ClientConfig {
|
|||
}
|
||||
|
||||
public static class DisplayConfig {
|
||||
public boolean fullscreen = true;
|
||||
public boolean captureCursor = true;
|
||||
public boolean fullscreen = false;
|
||||
public boolean captureCursor = false;
|
||||
public float fov = 70;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public class GameRenderer {
|
|||
private Model playerModel;
|
||||
private Model rifleModel;
|
||||
private Model blockModel;
|
||||
private Model bulletModel;
|
||||
|
||||
// Standard GUI textures.
|
||||
private GUITexture crosshairTexture;
|
||||
|
@ -132,6 +133,7 @@ public class GameRenderer {
|
|||
playerModel = new Model("model/player_simple.obj", "model/simple_player.png");
|
||||
rifleModel = new Model("model/rifle.obj", "model/rifle.png");
|
||||
blockModel = new Model("model/block.obj", "model/block.png");
|
||||
bulletModel = new Model("model/bullet.obj", "model/bullet.png");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -216,6 +218,16 @@ public class GameRenderer {
|
|||
}
|
||||
blockModel.unbind();
|
||||
|
||||
bulletModel.bind();
|
||||
Matrix4f projectileTransform = new Matrix4f();
|
||||
for (var projectile : client.getProjectiles().values()) {
|
||||
projectileTransform.identity()
|
||||
.translate(projectile.getPosition())
|
||||
.rotateTowards(projectile.getVelocity(), Camera.UP);
|
||||
modelRenderer.render(bulletModel, projectileTransform);
|
||||
}
|
||||
bulletModel.unbind();
|
||||
|
||||
modelRenderer.end();
|
||||
|
||||
// GUI rendering
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# Blender MTL File: 'bullet.blend'
|
||||
# Material Count: 1
|
||||
|
||||
newmtl Material.001
|
||||
Ns 225.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Kd 0.800000 0.800000 0.800000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.450000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd /home/andrew/Programming/github-andrewlalis/ace-of-shades-2/client/src/main/resources/model/bullet.png
|
|
@ -0,0 +1,140 @@
|
|||
# Blender v2.82 (sub 7) OBJ File: 'bullet.blend'
|
||||
# www.blender.org
|
||||
mtllib bullet.mtl
|
||||
o Cylinder
|
||||
v 0.000000 0.020000 -0.033933
|
||||
v 0.000000 0.020000 0.029329
|
||||
v 0.017321 0.010000 -0.033933
|
||||
v 0.017321 0.010000 0.029329
|
||||
v 0.017321 -0.010000 -0.033933
|
||||
v 0.017321 -0.010000 0.029329
|
||||
v -0.000000 -0.020000 -0.033933
|
||||
v -0.000000 -0.020000 0.029329
|
||||
v -0.017321 -0.010000 -0.033933
|
||||
v -0.017321 -0.010000 0.029329
|
||||
v -0.017321 0.010000 -0.033933
|
||||
v -0.017321 0.010000 0.029329
|
||||
v -0.000000 0.015593 0.042620
|
||||
v 0.013504 0.007796 0.042620
|
||||
v 0.013504 -0.007796 0.042620
|
||||
v -0.000000 -0.015593 0.042620
|
||||
v -0.013504 -0.007796 0.042620
|
||||
v -0.013504 0.007796 0.042620
|
||||
v -0.000000 0.004471 0.055747
|
||||
v 0.003872 0.002236 0.055747
|
||||
v 0.003872 -0.002236 0.055747
|
||||
v -0.000000 -0.004471 0.055747
|
||||
v -0.003872 -0.002236 0.055747
|
||||
v -0.003872 0.002236 0.055747
|
||||
vt 0.436144 0.309159
|
||||
vt 0.610602 1.000000
|
||||
vt 0.436144 1.000000
|
||||
vt 0.610602 0.309159
|
||||
vt 0.697830 0.973380
|
||||
vt 0.348915 0.717461
|
||||
vt 0.261686 0.000000
|
||||
vt 0.348915 0.026620
|
||||
vt 0.261686 0.690841
|
||||
vt 0.087229 0.000000
|
||||
vt 0.242465 0.841851
|
||||
vt 0.087229 0.690841
|
||||
vt 0.000000 0.026620
|
||||
vt 0.348915 0.282539
|
||||
vt 0.348915 0.973380
|
||||
vt 1.000000 0.110280
|
||||
vt 0.848915 0.441121
|
||||
vt 0.697831 0.110280
|
||||
vt 0.815623 0.441121
|
||||
vt 0.849399 0.588424
|
||||
vt 0.815623 0.563770
|
||||
vt 0.455366 0.158149
|
||||
vt 0.106450 0.841851
|
||||
vt 0.659387 0.137395
|
||||
vt 0.697830 0.282539
|
||||
vt 0.038443 0.862605
|
||||
vt 0.000000 0.717461
|
||||
vt 0.591380 0.158149
|
||||
vt 0.781846 0.637733
|
||||
vt 0.781846 0.588424
|
||||
vt 0.697831 0.699058
|
||||
vt 0.815623 0.662388
|
||||
vt 0.815623 0.785037
|
||||
vt 0.697831 0.527100
|
||||
vt 0.503872 0.000000
|
||||
vt 0.542874 0.000000
|
||||
vt 0.849399 0.637733
|
||||
vt 0.933415 0.699058
|
||||
vt 0.154956 1.000000
|
||||
vt 0.310472 0.862605
|
||||
vt 0.848915 0.000000
|
||||
vt 1.000000 0.330840
|
||||
vt 0.697831 0.330840
|
||||
vt 0.933415 0.527100
|
||||
vt 0.387358 0.137395
|
||||
vt 0.193959 1.000000
|
||||
vn 0.5000 0.8660 -0.0000
|
||||
vn 1.0000 0.0000 0.0000
|
||||
vn 0.5000 -0.8660 0.0000
|
||||
vn -0.5000 -0.8660 0.0000
|
||||
vn 0.4806 -0.8324 0.2760
|
||||
vn -1.0000 0.0000 0.0000
|
||||
vn -0.5000 0.8660 -0.0000
|
||||
vn -0.0000 0.0000 -1.0000
|
||||
vn 0.8062 0.0000 0.5916
|
||||
vn -0.4806 0.8324 0.2760
|
||||
vn -0.4806 -0.8324 0.2760
|
||||
vn 0.9612 0.0000 0.2760
|
||||
vn -0.9612 0.0000 0.2760
|
||||
vn 0.4806 0.8324 0.2760
|
||||
vn 0.0000 0.0000 1.0000
|
||||
vn -0.8062 0.0000 0.5916
|
||||
vn 0.4031 -0.6982 0.5916
|
||||
vn 0.4031 0.6982 0.5916
|
||||
vn -0.4031 0.6982 0.5916
|
||||
vn -0.4031 -0.6982 0.5916
|
||||
usemtl Material.001
|
||||
s off
|
||||
f 2/1/1 3/2/1 1/3/1
|
||||
f 4/4/2 5/5/2 3/2/2
|
||||
f 6/6/3 7/7/3 5/8/3
|
||||
f 8/9/4 9/10/4 7/7/4
|
||||
f 6/6/5 16/11/5 8/9/5
|
||||
f 10/12/6 11/13/6 9/10/6
|
||||
f 12/14/7 1/3/7 11/15/7
|
||||
f 3/16/8 7/17/8 11/18/8
|
||||
f 15/19/9 20/20/9 21/21/9
|
||||
f 12/14/10 13/22/10 2/1/10
|
||||
f 8/9/11 17/23/11 10/12/11
|
||||
f 4/4/12 15/24/12 6/25/12
|
||||
f 10/12/13 18/26/13 12/27/13
|
||||
f 4/4/14 13/22/14 14/28/14
|
||||
f 23/29/15 22/30/15 21/21/15
|
||||
f 17/31/16 24/32/16 18/33/16
|
||||
f 16/34/17 21/21/17 22/30/17
|
||||
f 14/28/18 19/35/18 20/36/18
|
||||
f 18/33/19 19/37/19 13/38/19
|
||||
f 16/11/20 23/39/20 17/23/20
|
||||
f 2/1/1 4/4/1 3/2/1
|
||||
f 4/4/2 6/25/2 5/5/2
|
||||
f 6/6/3 8/9/3 7/7/3
|
||||
f 8/9/4 10/12/4 9/10/4
|
||||
f 6/6/5 15/40/5 16/11/5
|
||||
f 10/12/6 12/27/6 11/13/6
|
||||
f 12/14/7 2/1/7 1/3/7
|
||||
f 11/18/8 1/41/8 3/16/8
|
||||
f 3/16/8 5/42/8 7/17/8
|
||||
f 7/17/8 9/43/8 11/18/8
|
||||
f 15/19/9 14/44/9 20/20/9
|
||||
f 12/14/10 18/45/10 13/22/10
|
||||
f 8/9/11 16/11/11 17/23/11
|
||||
f 4/4/12 14/28/12 15/24/12
|
||||
f 10/12/13 17/23/13 18/26/13
|
||||
f 4/4/14 2/1/14 13/22/14
|
||||
f 21/21/15 20/20/15 23/29/15
|
||||
f 20/20/15 19/37/15 23/29/15
|
||||
f 19/37/15 24/32/15 23/29/15
|
||||
f 17/31/16 23/29/16 24/32/16
|
||||
f 16/34/17 15/19/17 21/21/17
|
||||
f 14/28/18 13/22/18 19/35/18
|
||||
f 18/33/19 24/32/19 19/37/19
|
||||
f 16/11/20 22/46/20 23/39/20
|
After Width: | Height: | Size: 133 B |
|
@ -43,6 +43,7 @@ public final class Net {
|
|||
serializer.registerTypeSerializer(14, new ItemStackSerializer());
|
||||
serializer.registerType(15, InventorySelectedStackMessage.class);
|
||||
serializer.registerType(16, SoundMessage.class);
|
||||
serializer.registerType(17, ProjectileMessage.class);
|
||||
}
|
||||
|
||||
public static ExtendedDataInputStream getInputStream(InputStream in) {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package nl.andrewl.aos_core.model;
|
||||
|
||||
import org.joml.Vector3f;
|
||||
|
||||
/**
|
||||
* Represents a flying projectile object in the world (usually a bullet).
|
||||
* All projectiles have their orientation dependent on their velocity vector.
|
||||
*/
|
||||
public class Projectile {
|
||||
protected final int id;
|
||||
protected final Vector3f position;
|
||||
protected final Vector3f velocity;
|
||||
|
||||
public enum Type {BULLET}
|
||||
protected final Type type;
|
||||
|
||||
public Projectile(int id, Vector3f position, Vector3f velocity, Type type) {
|
||||
this.id = id;
|
||||
this.position = position;
|
||||
this.velocity = velocity;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Vector3f getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public Vector3f getVelocity() {
|
||||
return velocity;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewl.aos_core.model.item;
|
||||
|
||||
import nl.andrewl.aos_core.model.item.gun.Ak47;
|
||||
import nl.andrewl.aos_core.model.item.gun.Rifle;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
@ -14,10 +15,12 @@ public final class ItemTypes {
|
|||
|
||||
public static final BlockItem BLOCK = new BlockItem(1);
|
||||
public static final Rifle RIFLE = new Rifle(2);
|
||||
public static final Ak47 AK_47 = new Ak47(3);
|
||||
|
||||
static {
|
||||
registerType(BLOCK);
|
||||
registerType(RIFLE);
|
||||
registerType(AK_47);
|
||||
}
|
||||
|
||||
public static void registerType(Item type) {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package nl.andrewl.aos_core.model.item.gun;
|
||||
|
||||
import nl.andrewl.aos_core.model.item.Gun;
|
||||
|
||||
public class Ak47 extends Gun {
|
||||
public Ak47(int id) {
|
||||
super(
|
||||
id,
|
||||
"AK-47",
|
||||
4,
|
||||
30,
|
||||
1,
|
||||
0.95f,
|
||||
0.1f,
|
||||
1.2f,
|
||||
40f,
|
||||
30f,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
|
@ -129,7 +129,8 @@ public final class Worlds {
|
|||
}
|
||||
}
|
||||
|
||||
world.setSpawnPoint("first", new Vector3f(0.5f, 0f, 0.5f));
|
||||
world.setSpawnPoint("A", new Vector3f(0.5f, 0f, 0.5f));
|
||||
world.setSpawnPoint("B", new Vector3f(20.5f, 0f, 20.5f));
|
||||
|
||||
return world;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package nl.andrewl.aos_core.net.client;
|
||||
|
||||
import nl.andrewl.aos_core.model.Projectile;
|
||||
import nl.andrewl.record_net.Message;
|
||||
|
||||
/**
|
||||
* A simple message with an update about a bullet. Instead of having a separate
|
||||
* message to indicate that a bullet has been destroyed, we add it to the
|
||||
* normal bullet message.
|
||||
*/
|
||||
public record ProjectileMessage(
|
||||
int id, Projectile.Type type,
|
||||
float px, float py, float pz,
|
||||
float vx, float vy, float vz,
|
||||
boolean destroyed
|
||||
) implements Message {}
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewl.aos2_server;
|
||||
|
||||
import nl.andrewl.aos2_server.model.ServerPlayer;
|
||||
import nl.andrewl.aos_core.Net;
|
||||
import nl.andrewl.aos_core.model.item.ItemStack;
|
||||
import nl.andrewl.aos_core.model.world.Chunk;
|
||||
|
@ -163,7 +164,7 @@ public class ClientCommunicationHandler {
|
|||
WorldIO.write(server.getWorld(), out);
|
||||
|
||||
// Team data.
|
||||
var teams = server.getTeams().values();
|
||||
var teams = server.getTeamManager().getTeams();
|
||||
out.writeInt(teams.size());
|
||||
for (var team : teams) {
|
||||
out.writeInt(team.getId());
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package nl.andrewl.aos2_server;
|
||||
|
||||
import nl.andrewl.aos2_server.model.ServerPlayer;
|
||||
import nl.andrewl.aos_core.Net;
|
||||
import nl.andrewl.aos_core.model.Team;
|
||||
import nl.andrewl.aos_core.net.client.PlayerJoinMessage;
|
||||
|
@ -78,6 +79,15 @@ public class PlayerManager {
|
|||
return Collections.unmodifiableCollection(clientHandlers.values());
|
||||
}
|
||||
|
||||
public void tick(long currentTimeMillis, float dt) {
|
||||
for (var player : players.values()) {
|
||||
player.getActionManager().tick(currentTimeMillis, dt, server.getWorld(), server);
|
||||
if (player.getActionManager().isUpdated()) {
|
||||
broadcastUdpMessage(player.getUpdateMessage(currentTimeMillis));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the team that's best suited for adding a new player. This is the
|
||||
* team that has the minimum (or tied for minimum) number of players.
|
||||
|
@ -86,7 +96,7 @@ public class PlayerManager {
|
|||
private Team findBestTeamForNewPlayer() {
|
||||
Team minTeam = null;
|
||||
int minCount = Integer.MAX_VALUE;
|
||||
for (var team : server.getTeams().values()) {
|
||||
for (var team : server.getTeamManager().getTeams()) {
|
||||
int playerCount = (int) players.values().stream()
|
||||
.filter(p -> Objects.equals(p.getTeam(), team))
|
||||
.count();
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package nl.andrewl.aos2_server;
|
||||
|
||||
import nl.andrewl.aos2_server.model.ServerPlayer;
|
||||
import nl.andrewl.aos2_server.model.ServerProjectile;
|
||||
import nl.andrewl.aos_core.model.Projectile;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
public class ProjectileManager {
|
||||
private final Server server;
|
||||
private int nextProjectileId = 1;
|
||||
private final Map<Integer, ServerProjectile> projectiles;
|
||||
private final Queue<ServerProjectile> removalQueue;
|
||||
|
||||
public ProjectileManager(Server server) {
|
||||
this.server = server;
|
||||
this.projectiles = new HashMap<>();
|
||||
this.removalQueue = new LinkedList<>();
|
||||
}
|
||||
|
||||
public void spawnBullet(ServerPlayer player) {
|
||||
int id = nextProjectileId++;
|
||||
if (nextProjectileId == Integer.MAX_VALUE) nextProjectileId = 1;
|
||||
Vector3f pos = new Vector3f(player.getEyePosition());
|
||||
Vector3f vel = new Vector3f(player.getViewVector()).normalize().mul(300);
|
||||
ServerProjectile bullet = new ServerProjectile(id, pos, vel, Projectile.Type.BULLET, player);
|
||||
projectiles.put(bullet.getId(), bullet);
|
||||
server.getPlayerManager().broadcastUdpMessage(bullet.toMessage(false));
|
||||
}
|
||||
|
||||
public void tick(float dt) {
|
||||
for (var projectile : projectiles.values()) {
|
||||
tickProjectile(projectile, dt);
|
||||
}
|
||||
while (!removalQueue.isEmpty()) {
|
||||
ServerProjectile projectile = removalQueue.remove();
|
||||
projectiles.remove(projectile.getId());
|
||||
}
|
||||
}
|
||||
|
||||
private void tickProjectile(ServerProjectile projectile, float dt) {
|
||||
projectile.getVelocity().y -= server.getConfig().physics.gravity * dt;
|
||||
// TODO: Check if bullet will hit anything, like blocks or players, if it follows current velocity.
|
||||
Vector3f movement = new Vector3f(projectile.getVelocity()).mul(dt);
|
||||
|
||||
projectile.getPosition().add(movement);
|
||||
if (projectile.getDistanceTravelled() > 500) {
|
||||
removalQueue.add(projectile);
|
||||
server.getPlayerManager().broadcastUdpMessage(projectile.toMessage(true));
|
||||
} else {
|
||||
server.getPlayerManager().broadcastUdpMessage(projectile.toMessage(false));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,8 +2,8 @@ package nl.andrewl.aos2_server;
|
|||
|
||||
import nl.andrewl.aos2_server.config.ServerConfig;
|
||||
import nl.andrewl.aos2_server.logic.WorldUpdater;
|
||||
import nl.andrewl.aos2_server.model.ServerPlayer;
|
||||
import nl.andrewl.aos_core.config.Config;
|
||||
import nl.andrewl.aos_core.model.Team;
|
||||
import nl.andrewl.aos_core.model.world.World;
|
||||
import nl.andrewl.aos_core.model.world.Worlds;
|
||||
import nl.andrewl.aos_core.net.UdpReceiver;
|
||||
|
@ -18,7 +18,7 @@ import org.slf4j.LoggerFactory;
|
|||
import java.io.IOException;
|
||||
import java.net.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
|
||||
public class Server implements Runnable {
|
||||
|
@ -29,8 +29,8 @@ public class Server implements Runnable {
|
|||
private volatile boolean running;
|
||||
private final ServerConfig config;
|
||||
private final PlayerManager playerManager;
|
||||
private final Map<Integer, Team> teams;
|
||||
private final Map<Team, String> teamSpawnPoints;
|
||||
private final TeamManager teamManager;
|
||||
private final ProjectileManager projectileManager;
|
||||
private final World world;
|
||||
private final WorldUpdater worldUpdater;
|
||||
|
||||
|
@ -41,13 +41,14 @@ public class Server implements Runnable {
|
|||
this.datagramSocket = new DatagramSocket(config.port);
|
||||
this.datagramSocket.setReuseAddress(true);
|
||||
this.playerManager = new PlayerManager(this);
|
||||
this.teamManager = new TeamManager(this);
|
||||
this.projectileManager = new ProjectileManager(this);
|
||||
this.worldUpdater = new WorldUpdater(this, config.ticksPerSecond);
|
||||
this.world = Worlds.testingWorld();
|
||||
this.teams = new HashMap<>();
|
||||
this.teamSpawnPoints = new HashMap<>();
|
||||
// TODO: Add some way to configure teams with config files.
|
||||
teams.put(1, new Team(1, "Red", new Vector3f(0.8f, 0, 0), world.getSpawnPoint("first")));
|
||||
teams.put(2, new Team(2, "Blue", new Vector3f(0, 0, 0.8f), world.getSpawnPoint("first")));
|
||||
|
||||
// TODO: Add some way to configure teams with config files or commands.
|
||||
teamManager.addTeam("Red", new Vector3f(0.8f, 0, 0), "A");
|
||||
teamManager.addTeam("Blue", new Vector3f(0, 0, 0.8f), "B");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -121,16 +122,12 @@ public class Server implements Runnable {
|
|||
return playerManager;
|
||||
}
|
||||
|
||||
public Map<Integer, Team> getTeams() {
|
||||
return teams;
|
||||
public TeamManager getTeamManager() {
|
||||
return teamManager;
|
||||
}
|
||||
|
||||
public String getSpawnPoint(Team team) {
|
||||
return teamSpawnPoints.get(team);
|
||||
}
|
||||
|
||||
public Collection<ServerPlayer> getPlayersInTeam(Team team) {
|
||||
return playerManager.getPlayers().stream().filter(p -> Objects.equals(p.getTeam(), team)).toList();
|
||||
public ProjectileManager getProjectileManager() {
|
||||
return projectileManager;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package nl.andrewl.aos2_server;
|
||||
|
||||
import nl.andrewl.aos2_server.model.ServerPlayer;
|
||||
import nl.andrewl.aos_core.model.Team;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Component that manages the teams on a server.
|
||||
*/
|
||||
public class TeamManager {
|
||||
private int nextTeamId = 1;
|
||||
private final Server server;
|
||||
private final Map<Integer, Team> teams;
|
||||
|
||||
public TeamManager(Server server) {
|
||||
this.server = server;
|
||||
this.teams = new HashMap<>();
|
||||
}
|
||||
|
||||
public synchronized void addTeam(String name, Vector3f color, String spawnPoint) {
|
||||
int id = nextTeamId++;
|
||||
teams.put(id, new Team(id, name, color, server.getWorld().getSpawnPoint(spawnPoint)));
|
||||
}
|
||||
|
||||
public Team getTeam(int id) {
|
||||
return teams.get(id);
|
||||
}
|
||||
|
||||
public Collection<Team> getTeams() {
|
||||
return Collections.unmodifiableCollection(teams.values());
|
||||
}
|
||||
|
||||
public Collection<ServerPlayer> getPlayers(Team team) {
|
||||
return server.getPlayerManager().getPlayers().stream()
|
||||
.filter(p -> p.getTeam().equals(team))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Collection<ServerPlayer> getPlayers(int id) {
|
||||
Team team = getTeam(id);
|
||||
if (team == null) return Collections.emptyList();
|
||||
return getPlayers(team);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
package nl.andrewl.aos2_server.logic;
|
||||
|
||||
import nl.andrewl.aos2_server.Server;
|
||||
import nl.andrewl.aos2_server.ServerPlayer;
|
||||
import nl.andrewl.aos2_server.model.ServerPlayer;
|
||||
import nl.andrewl.aos2_server.config.ServerConfig;
|
||||
import nl.andrewl.aos_core.model.item.*;
|
||||
import nl.andrewl.aos_core.model.world.World;
|
||||
|
@ -18,7 +18,7 @@ import org.joml.Vector3i;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static nl.andrewl.aos2_server.ServerPlayer.*;
|
||||
import static nl.andrewl.aos2_server.model.ServerPlayer.*;
|
||||
|
||||
/**
|
||||
* Component that manages a server player's current actions and movement.
|
||||
|
@ -62,9 +62,8 @@ public class PlayerActionManager {
|
|||
return updated;
|
||||
}
|
||||
|
||||
public void tick(float dt, World world, Server server) {
|
||||
public void tick(long now, float dt, World world, Server server) {
|
||||
updated = false; // Reset the updated flag. This will be set to true if the player was updated in this tick.
|
||||
long now = System.currentTimeMillis();
|
||||
if (player.getInventory().getSelectedIndex() != lastInputState.selectedInventoryIndex()) {
|
||||
player.getInventory().setSelectedIndex(lastInputState.selectedInventoryIndex());
|
||||
// Tell the client that their inventory slot has been updated properly.
|
||||
|
@ -100,12 +99,7 @@ public class PlayerActionManager {
|
|||
now - gunLastShotAt > gun.getShotCooldownTime() * 1000 &&
|
||||
(gun.isAutomatic() || !gunNeedsReCock)
|
||||
) {
|
||||
// TODO: trace a ray from gun to see if players intersect with it.
|
||||
var hit = world.getLookingAtPos(player.getEyePosition(), player.getViewVector(), 100);
|
||||
if (hit != null) {
|
||||
world.setBlockAt(hit.pos().x, hit.pos().y, hit.pos().z, (byte) 0);
|
||||
server.getPlayerManager().broadcastUdpMessage(ChunkUpdateMessage.fromWorld(hit.pos(), world));
|
||||
}
|
||||
server.getProjectileManager().spawnBullet(player);
|
||||
g.setBulletCount(g.getBulletCount() - 1);
|
||||
gunLastShotAt = now;
|
||||
if (!gun.isAutomatic()) {
|
||||
|
|
|
@ -52,12 +52,14 @@ public class WorldUpdater implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main server update method, that runs once on each game tick, and
|
||||
* performs all game state updates.
|
||||
* @param currentTimeMillis The current timestamp for the tick. This may
|
||||
* be needed for certain functions in logic.
|
||||
*/
|
||||
private void tick(long currentTimeMillis) {
|
||||
for (var player : server.getPlayerManager().getPlayers()) {
|
||||
player.getActionManager().tick(secondsPerTick, server.getWorld(), server);
|
||||
if (player.getActionManager().isUpdated()) {
|
||||
server.getPlayerManager().broadcastUdpMessage(player.getUpdateMessage(currentTimeMillis));
|
||||
}
|
||||
}
|
||||
server.getPlayerManager().tick(currentTimeMillis, secondsPerTick);
|
||||
server.getProjectileManager().tick(secondsPerTick);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package nl.andrewl.aos2_server;
|
||||
package nl.andrewl.aos2_server.model;
|
||||
|
||||
import nl.andrewl.aos2_server.logic.PlayerActionManager;
|
||||
import nl.andrewl.aos_core.model.Player;
|
||||
|
@ -26,8 +26,9 @@ public class ServerPlayer extends Player {
|
|||
super(id, username);
|
||||
this.inventory = new Inventory(new ArrayList<>(), 0);
|
||||
this.actionManager = new PlayerActionManager(this);
|
||||
inventory.getItemStacks().add(new GunItemStack(ItemTypes.get("Rifle")));
|
||||
inventory.getItemStacks().add(new BlockItemStack(ItemTypes.get("Block"), 50, (byte) 1));
|
||||
inventory.getItemStacks().add(new GunItemStack(ItemTypes.RIFLE));
|
||||
inventory.getItemStacks().add(new BlockItemStack(ItemTypes.BLOCK, 50, (byte) 1));
|
||||
inventory.getItemStacks().add(new GunItemStack(ItemTypes.AK_47));
|
||||
}
|
||||
|
||||
public PlayerActionManager getActionManager() {
|
|
@ -0,0 +1,42 @@
|
|||
package nl.andrewl.aos2_server.model;
|
||||
|
||||
import nl.andrewl.aos_core.model.Projectile;
|
||||
import nl.andrewl.aos_core.net.client.ProjectileMessage;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
/**
|
||||
* A bullet with extra information about who shot it, and from where it was
|
||||
* shot, so we can properly delete the bullet and know who is responsible
|
||||
* for damage it causes.
|
||||
*/
|
||||
public class ServerProjectile extends Projectile {
|
||||
private final ServerPlayer player;
|
||||
private final Vector3f origin;
|
||||
|
||||
public ServerProjectile(int id, Vector3f position, Vector3f velocity, Type type, ServerPlayer player) {
|
||||
super(id, position, velocity, type);
|
||||
this.player = player;
|
||||
this.origin = new Vector3f(position);
|
||||
}
|
||||
|
||||
public ServerPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public Vector3f getOrigin() {
|
||||
return origin;
|
||||
}
|
||||
|
||||
public float getDistanceTravelled() {
|
||||
return origin.distance(position);
|
||||
}
|
||||
|
||||
public ProjectileMessage toMessage(boolean destroyed) {
|
||||
return new ProjectileMessage(
|
||||
id, type,
|
||||
position.x, position.y, position.z,
|
||||
velocity.x, velocity.y, velocity.z,
|
||||
destroyed
|
||||
);
|
||||
}
|
||||
}
|