Răsfoiți Sursa

Working multiplayer

Tankernn 7 ani în urmă
părinte
comite
508fcf40c9

+ 45 - 12
src/main/java/eu/tankernn/game/server/GameServer.java

@@ -1,32 +1,62 @@
 package eu.tankernn.game.server;
 
-import java.util.ArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.lwjgl.util.vector.Vector3f;
 
-import eu.tankernn.gameEngine.entities.Entity3D;
 import eu.tankernn.gameEngine.entities.Light;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.EventLoopGroup;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.ChannelGroupFuture;
+import io.netty.channel.group.DefaultChannelGroup;
 import io.netty.channel.nio.NioEventLoopGroup;
 import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
 import io.netty.handler.codec.serialization.ClassResolvers;
 import io.netty.handler.codec.serialization.ObjectDecoder;
 import io.netty.handler.codec.serialization.ObjectEncoder;
-import io.netty.handler.timeout.IdleStateHandler;
 import io.netty.handler.timeout.ReadTimeoutHandler;
+import io.netty.util.concurrent.GlobalEventExecutor;
 
 public class GameServer {
+	private ScheduledExecutorService executor;
+
 	private int port;
 	private World world;
+	private ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
 
 	public GameServer(int port) {
 		this.port = port;
-		this.world = new World(1337, new ArrayList<Light>(), new ArrayList<Entity3D>());
+
+		executor = Executors.newSingleThreadScheduledExecutor();
+
+		this.world = new World(1337);
+		world.getState().getLights().add(new Light(new Vector3f(1000, 1000, 0), new Vector3f(1, 1, 1)));
+		startListening();
+		executor.scheduleAtFixedRate(this::update, 0, 1000 / 16, TimeUnit.MILLISECONDS); // 64
+																							// tick
+																							// XD
+	}
+
+	private void update() {
+		try {
+			ChannelGroupFuture f = channelGroup.flushAndWrite(world.getState()).sync();
+			if (!f.isSuccess()) {
+				f.cause().printStackTrace();
+			}
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		} catch (Throwable t) {
+			t.printStackTrace();
+		}
 	}
 
-	public void run() throws Exception {
+	public void startListening() {
 		EventLoopGroup bossGroup = new NioEventLoopGroup();
 		EventLoopGroup workerGroup = new NioEventLoopGroup();
 		ServerBootstrap b = new ServerBootstrap();
@@ -34,19 +64,22 @@ public class GameServer {
 				.childHandler(new ChannelInitializer<SocketChannel>() {
 					@Override
 					public void initChannel(SocketChannel ch) throws Exception {
-						
+
 						ch.pipeline().addLast("objectDecoder", new ObjectDecoder(ClassResolvers.cacheDisabled(null)));
 						ch.pipeline().addLast("objectEncoder", new ObjectEncoder());
-						ch.pipeline().addLast("timeouthandler", new ReadTimeoutHandler(30));
-						ch.pipeline().addLast(new IdleStateHandler(0, 0, 29));
+						ch.pipeline().addLast("timeouthandler", new ReadTimeoutHandler(10));
 
-						ch.pipeline().addLast(new GameServerHandler(world));
+						ch.pipeline().addLast(new GameServerHandler(channelGroup, world));
 					}
 				}).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true);
 
 		// Bind and start to accept incoming connections.
-		b.bind(port).sync();
-		
+		try {
+			b.bind(port).sync();
+		} catch (InterruptedException e) {
+			e.printStackTrace();
+		}
+
 		System.out.println("Server started.");
 	}
 
@@ -58,7 +91,7 @@ public class GameServer {
 			port = 25566;
 		}
 		try {
-			new GameServer(port).run();
+			new GameServer(port);
 		} catch (Exception e) {
 			e.printStackTrace();
 		}

+ 26 - 15
src/main/java/eu/tankernn/game/server/GameServerHandler.java

@@ -1,39 +1,50 @@
 package eu.tankernn.game.server;
 
-import org.lwjgl.util.vector.ReadableVector3f;
-import org.lwjgl.util.vector.Vector3f;
-
 import eu.tankernn.game.server.entities.player.ServerPlayer;
+import eu.tankernn.gameEngine.entities.EntityState;
+import eu.tankernn.gameEngine.multiplayer.LoginRequest;
+import eu.tankernn.gameEngine.multiplayer.LoginResponse;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
-import io.netty.handler.timeout.IdleStateEvent;
+import io.netty.channel.group.ChannelGroup;
 
 public class GameServerHandler extends ChannelInboundHandlerAdapter {
 
 	private ServerPlayer player;
 	private final World world;
+	private final ChannelGroup group;
 
-	public GameServerHandler(World world) {
+	public GameServerHandler(ChannelGroup group, World world) {
+		this.group = group;
 		this.world = world;
 	}
 
+	@Override
+	public void channelActive(ChannelHandlerContext ctx) throws Exception {
+		player = new ServerPlayer(ctx.channel());
+	}
+
 	@Override
 	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
-		if (player == null && msg instanceof String) {
-			player = new ServerPlayer((String) msg);
-			world.players.add(player);
+		if (msg instanceof LoginRequest) {
+			LoginRequest request = (LoginRequest) msg;
+			player.setUsername(request.username);
+			world.addPlayer(player);
+			group.add(ctx.channel());
 			System.out.println("Player connected with username: " + player.getUsername());
-			ctx.writeAndFlush(Integer.valueOf(world.seed));
-		} else if (msg instanceof Vector3f) {
-			player.getPosition().set((ReadableVector3f) msg);
+			ctx.writeAndFlush(new LoginResponse(true, player.getState())).sync();
+		} else if (msg instanceof EntityState) {
+			EntityState state = (EntityState) msg;
+			player.setState(state);
+		} else {
+			System.err.println("Unknown message: " + msg.toString());
 		}
 	}
 
 	@Override
-	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
-		if (evt instanceof IdleStateEvent) {
-			ctx.writeAndFlush(new Object());
-		}
+	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+		world.removePlayer(player);
+		group.remove(ctx.channel());
 	}
 
 	@Override

+ 31 - 11
src/main/java/eu/tankernn/game/server/World.java

@@ -2,22 +2,42 @@ package eu.tankernn.game.server;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
+import eu.tankernn.game.server.entities.ServerEntity;
 import eu.tankernn.game.server.entities.player.ServerPlayer;
-import eu.tankernn.gameEngine.entities.Entity3D;
-import eu.tankernn.gameEngine.entities.Light;
+import eu.tankernn.gameEngine.entities.EntityState;
+import eu.tankernn.gameEngine.multiplayer.WorldState;
 
 public class World {
-	public final int seed;
-	List<Light> lights;
-	List<Entity3D> entities;
-	List<ServerPlayer> players;
+	private WorldState state;
+	private final List<ServerEntity> entities = new ArrayList<>();
+	private final List<ServerPlayer> players = new ArrayList<>();
 	
-	public World(int seed, List<Light> lights, List<Entity3D> entities) {
-		this.seed = seed;
-		this.lights = lights;
-		this.entities = entities;
-		this.players = new ArrayList<ServerPlayer>();
+	public World(int seed) {
+		this.state = new WorldState(seed);
+	}
+	
+	public void update() {
+		state.getEntities().stream().forEach(EntityState::update);
+	}
+	
+	public WorldState getState() {
+		return new WorldState(state.getSeed(), state.getLights(), entities.stream().map(ServerEntity::getState).collect(Collectors.toList()));
+	}
+
+	public List<ServerPlayer> getPlayers() {
+		return players;
+	}
+
+	public void addPlayer(ServerPlayer player) {
+		players.add(player);
+		entities.add(player);
+	}
+
+	public void removePlayer(ServerPlayer player) {
+		players.remove(player);
+		entities.remove(player);
 	}
 	
 	

+ 19 - 0
src/main/java/eu/tankernn/game/server/entities/ServerEntity.java

@@ -0,0 +1,19 @@
+package eu.tankernn.game.server.entities;
+
+import eu.tankernn.gameEngine.entities.EntityState;
+
+public class ServerEntity {
+	protected EntityState state;
+
+	public EntityState getState() {
+		return state;
+	}
+
+	public void setState(EntityState state) {
+		this.state = state;
+	}
+	
+	public void update() {
+		state.update();
+	}
+}

+ 22 - 10
src/main/java/eu/tankernn/game/server/entities/player/ServerPlayer.java

@@ -1,21 +1,33 @@
 package eu.tankernn.game.server.entities.player;
 
-import org.lwjgl.util.vector.Vector3f;
+import eu.tankernn.game.server.entities.ServerEntity;
+import eu.tankernn.gameEngine.entities.EntityState;
+import io.netty.channel.Channel;
 
-public class ServerPlayer {
-	private final Vector3f position;
+public class ServerPlayer extends ServerEntity {
+	
+	private final Channel channel;
 	private String username;
 	
-	public ServerPlayer(String username) {
+	public ServerPlayer(Channel channel) {
+		this.state = new EntityState();
+		state.setModelId(0);
+		this.channel = channel;
+	}
+	
+	public String getUsername() {
+		return username;
+	}
+	
+	public void setUsername(String username) {
 		this.username = username;
-		this.position = new Vector3f();
 	}
-
-	public Vector3f getPosition() {
-		return position;
+	
+	public Channel getChannel() {
+		return channel;
 	}
 
-	public String getUsername() {
-		return username;
+	public int getId() {
+		return state.getId();
 	}
 }