diff --git a/README.md b/README.md index 69a8d11..71bc744 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,14 @@ Setting up a server is quite easy. Just go to the [releases page](https://github 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 diff --git a/core/src/main/java/nl/andrewl/aos_core/model/world/ColorPalette.java b/core/src/main/java/nl/andrewl/aos_core/model/world/ColorPalette.java index 6b348ab..ac7cffa 100644 --- a/core/src/main/java/nl/andrewl/aos_core/model/world/ColorPalette.java +++ b/core/src/main/java/nl/andrewl/aos_core/model/world/ColorPalette.java @@ -24,10 +24,19 @@ public class ColorPalette { } public void setColor(byte value, float r, float g, float b) { - if (value < 0) return; + if (value <= 0) return; colors[value - 1].set(r, g, b); } + public void setColor(byte value, Color color) { + if (value <= 0) return; + colors[value - 1].set( + color.getRed() / 255f, + color.getGreen() / 255f, + color.getBlue() / 255f + ); + } + public float[] toArray() { float[] array = new float[3 * MAX_COLORS]; for (int i = 0; i < MAX_COLORS; i++) { diff --git a/core/src/main/java/nl/andrewl/aos_core/model/world/WorldIO.java b/core/src/main/java/nl/andrewl/aos_core/model/world/WorldIO.java index 1b34dc3..abe0f97 100644 --- a/core/src/main/java/nl/andrewl/aos_core/model/world/WorldIO.java +++ b/core/src/main/java/nl/andrewl/aos_core/model/world/WorldIO.java @@ -3,6 +3,8 @@ package nl.andrewl.aos_core.model.world; import org.joml.Vector3f; import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Map; /** @@ -43,6 +45,12 @@ public final class WorldIO { } } + public static void write(World world, Path filePath) throws IOException { + try (var out = Files.newOutputStream(filePath)) { + write(world, out); + } + } + /** * Reads a world from an input stream. * @param in The input stream to read from. @@ -78,4 +86,10 @@ public final class WorldIO { } return world; } + + public static World read(Path filePath) throws IOException { + try (var in = Files.newInputStream(filePath)) { + return read(in); + } + } } diff --git a/core/src/main/java/nl/andrewl/aos_core/model/world/Worlds.java b/core/src/main/java/nl/andrewl/aos_core/model/world/Worlds.java index eab21d9..f653b62 100644 --- a/core/src/main/java/nl/andrewl/aos_core/model/world/Worlds.java +++ b/core/src/main/java/nl/andrewl/aos_core/model/world/Worlds.java @@ -3,7 +3,9 @@ package nl.andrewl.aos_core.model.world; import org.joml.Vector3f; import org.joml.Vector3i; +import java.awt.*; import java.util.Collections; +import java.util.Random; /** * Simple container for a bunch of static methods for creating pre-made worlds @@ -25,7 +27,9 @@ public final class Worlds { } } } - return new World(ColorPalette.rainbow(), Collections.singleton(chunk)); + World world = new World(ColorPalette.rainbow(), Collections.singleton(chunk)); + world.setSpawnPoint("A", new Vector3f(Chunk.SIZE / 2f, Chunk.SIZE / 2f, Chunk.SIZE / 2f)); + return world; } /** @@ -151,4 +155,50 @@ public final class Worlds { return world; } + + /** + * A square arena for up to 4 teams, with spawn points A, B, C, and D. + * @return The world. + */ + public static World arena() { + World world = new World(); + ColorPalette palette = new ColorPalette(); + palette.setColor((byte) 1, Color.BLACK); + palette.setColor((byte) 2, Color.WHITE); + palette.setColor((byte) 3, Color.GRAY); + palette.setColor((byte) 4, Color.DARK_GRAY); + palette.setColor((byte) 5, new Color(79, 58, 0)); + palette.setColor((byte) 6, new Color(133, 118, 78)); + palette.setColor((byte) 7, new Color(69, 59, 32)); + palette.setColor((byte) 8, new Color(38, 77, 30)); + palette.setColor((byte) 9, new Color(4, 107, 40)); + palette.setColor((byte) 10, Color.RED.darker()); + palette.setColor((byte) 11, Color.GREEN.darker()); + palette.setColor((byte) 12, Color.BLUE.darker()); + world.setPalette(palette); + Random rand = new Random(1L); + for (int cx = 0; cx < 9; cx++) { + for (int cz = 0; cz < 9; cz++) { + for (int cy = 0; cy < 5; cy++) { + world.addChunk(new Chunk(cx, cy, cz)); + } + } + } + int surface = 3 * Chunk.SIZE; + for (int x = world.getMinX(); x < world.getMaxX(); x++) { + for (int z = world.getMinZ(); z < world.getMaxZ(); z++) { + for (int y = 0; y < surface - 1; y++) { + world.setBlockAt(x, y, z, (byte) rand.nextInt(5, 8)); + } + world.setBlockAt(x, surface - 1, z, (byte) rand.nextInt(8, 10)); + } + } + + world.setSpawnPoint("A", new Vector3f(5.5f, surface, 5.5f)); + world.setSpawnPoint("C", new Vector3f(world.getMaxX() - 5.5f, surface, 5.5f)); + world.setSpawnPoint("D", new Vector3f(5.5f, surface, world.getMaxZ() - 5.5f)); + world.setSpawnPoint("B", new Vector3f(world.getMaxX() - 5.5f, surface, world.getMaxZ() - 5.5f)); + + return world; + } } diff --git a/server/src/main/java/nl/andrewl/aos2_server/Server.java b/server/src/main/java/nl/andrewl/aos2_server/Server.java index a692990..85edc41 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/Server.java +++ b/server/src/main/java/nl/andrewl/aos2_server/Server.java @@ -4,9 +4,11 @@ import nl.andrewl.aos2_server.cli.ServerCli; 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.FileUtils; import nl.andrewl.aos_core.config.Config; import nl.andrewl.aos_core.model.item.BlockItemStack; import nl.andrewl.aos_core.model.world.World; +import nl.andrewl.aos_core.model.world.WorldIO; import nl.andrewl.aos_core.model.world.Worlds; import nl.andrewl.aos_core.net.UdpReceiver; import nl.andrewl.aos_core.net.client.BlockColorMessage; @@ -20,6 +22,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.*; +import java.nio.file.Files; import java.nio.file.Path; import java.util.List; import java.util.concurrent.ForkJoinPool; @@ -47,11 +50,29 @@ public class Server implements Runnable { this.teamManager = new TeamManager(this); this.projectileManager = new ProjectileManager(this); this.worldUpdater = new WorldUpdater(this, config.ticksPerSecond); - this.world = Worlds.testingWorld(); - // 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"); + if (config.world.startsWith("worlds.")) { + String worldName = config.world.substring("worlds.".length()); + this.world = switch (worldName) { + case "testing" -> Worlds.testingWorld(); + case "flat" -> Worlds.flatWorld(); + case "cube" -> Worlds.smallCube(); + case "arena" -> Worlds.arena(); + default -> WorldIO.read(FileUtils.getClasspathResource("redfort.wld")); + }; + } else { + Path worldFile = Path.of(config.world); + if (Files.isReadable(worldFile)) { + this.world = WorldIO.read(worldFile); + } else { + log.error("Cannot read world file: {}", worldFile.toAbsolutePath()); + this.world = Worlds.arena(); + } + } + + for (var teamConfig : config.teams) { + teamManager.addTeam(teamConfig.name, new Vector3f(teamConfig.color), teamConfig.spawnPoint); + } } @Override diff --git a/server/src/main/java/nl/andrewl/aos2_server/cli/SaveWorldCommand.java b/server/src/main/java/nl/andrewl/aos2_server/cli/SaveWorldCommand.java new file mode 100644 index 0000000..e0191b4 --- /dev/null +++ b/server/src/main/java/nl/andrewl/aos2_server/cli/SaveWorldCommand.java @@ -0,0 +1,30 @@ +package nl.andrewl.aos2_server.cli; + +import nl.andrewl.aos_core.model.world.WorldIO; +import picocli.CommandLine; + +import java.io.IOException; +import java.nio.file.Path; + +@CommandLine.Command( + name = "save-world", + description = "Saves the current world to a file.", + mixinStandardHelpOptions = true +) +public class SaveWorldCommand implements Runnable { + @CommandLine.ParentCommand ServerCli cli; + + @CommandLine.Option(names = {"-o", "--output"}, description = "The file to save to.", defaultValue = "world.wld") + Path file; + + @Override + public void run() { + try { + cli.out.println("Saving world..."); + WorldIO.write(cli.server.getWorld(), file); + cli.out.println("Saved server's world to " + file.toAbsolutePath()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/server/src/main/java/nl/andrewl/aos2_server/cli/ServerCli.java b/server/src/main/java/nl/andrewl/aos2_server/cli/ServerCli.java index 1d87c95..818bb67 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/cli/ServerCli.java +++ b/server/src/main/java/nl/andrewl/aos2_server/cli/ServerCli.java @@ -22,7 +22,7 @@ import java.nio.file.Path; name = "", description = "Interactive shell for server commands.", footer = {"", "Press Ctrl-D to exit."}, - subcommands = {StopCommand.class, PlayersCommand.class} + subcommands = {StopCommand.class, PlayersCommand.class, SaveWorldCommand.class} ) public class ServerCli implements Runnable { final Server server; diff --git a/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java b/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java index dd6e0d9..99e36fe 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java +++ b/server/src/main/java/nl/andrewl/aos2_server/config/ServerConfig.java @@ -4,8 +4,13 @@ public class ServerConfig { public int port = 25565; public int connectionBacklog = 5; public float ticksPerSecond = 20.0f; + public String world = "worlds.redfort"; public PhysicsConfig physics = new PhysicsConfig(); public ActionsConfig actions = new ActionsConfig(); + public TeamConfig[] teams = new TeamConfig[]{ + new TeamConfig("Red", new float[]{0.8f, 0, 0}, "A"), + new TeamConfig("Blue", new float[]{0, 0, 0.8f}, "B") + }; public static class PhysicsConfig { public float gravity = 9.81f * 3; @@ -30,4 +35,18 @@ public class ServerConfig { public float movementAccuracyDecreaseFactor = 0.01f; public boolean friendlyFire = false; } + + public static class TeamConfig { + public String name; + public float[] color; + public String spawnPoint; + + public TeamConfig() {} + + public TeamConfig(String name, float[] color, String spawnPoint) { + this.name = name; + this.color = color; + this.spawnPoint = spawnPoint; + } + } } diff --git a/server/src/main/resources/redfort.wld b/server/src/main/resources/redfort.wld new file mode 100644 index 0000000..073b6b1 Binary files /dev/null and b/server/src/main/resources/redfort.wld differ