package org.jrenner.fps.net; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.TimeUtils; import org.jrenner.fps.Direction; import org.jrenner.fps.GameWorld; import org.jrenner.fps.Log; import org.jrenner.fps.Main; import org.jrenner.fps.Player; import org.jrenner.fps.entity.DynamicEntity; import org.jrenner.fps.entity.Entity; import org.jrenner.fps.event.ServerEvent; import org.jrenner.fps.event.ServerEventManager; import org.jrenner.fps.net.client.ClientUpdate; import org.jrenner.fps.net.packages.ChatMessage; import org.jrenner.fps.net.packages.CommandPackage; import org.jrenner.fps.utils.Pooler; import static org.jrenner.fps.net.packages.CommandPackage.BACK; import static org.jrenner.fps.net.packages.CommandPackage.DOWN; import static org.jrenner.fps.net.packages.CommandPackage.FORWARD; import static org.jrenner.fps.net.packages.CommandPackage.JUMP; import static org.jrenner.fps.net.packages.CommandPackage.SHOOT; import static org.jrenner.fps.net.packages.CommandPackage.STRAFE_LEFT; import static org.jrenner.fps.net.packages.CommandPackage.STRAFE_RIGHT; import static org.jrenner.fps.net.packages.CommandPackage.UP; public abstract class AbstractServer { protected Array<ClientUpdate> updateQueue = new Array<>(); long lastFrameTime = System.currentTimeMillis(); long accumulatedTime; public int tickNum; public static final long tickInterval = 50; // ms public static final String serverName = "Test Server"; public static final String serverMsg = "Welcome to the test server"; protected final Array<Integer> destroyedEntityQueue = new Array<>(); protected final Array<ChatMessage> chatQueue = new Array<>(); public ServerEventManager serverEventManager = new ServerEventManager(); public void handleClientUpdate(ClientUpdate clientUpdate) { synchronized (updateQueue) { updateQueue.add(clientUpdate); } } private Vector3 transVec = new Vector3(); private Vector3 rotVec = new Vector3(); /** Server handles incoming data from clients here */ public void processClientUpdates() { //Log.debug("processing client updates: " + updateQueue.size); synchronized (updateQueue) { for (ClientUpdate update : updateQueue) { if (update == null) { Log.debug("null update! skipping"); return; } int id = update.entityId; Entity ent = Entity.getEntityById(id); if (ent == null) { Log.debug("entity is null, not processing update"); return; } CommandPackage cmd = update.cmdPack; transVec.setZero(); rotVec.setZero(); int bits = cmd.commandBits; // Translation if ((bits & FORWARD) != 0) addTranslation(Direction.Translation.Forward); if ((bits & BACK) != 0) addTranslation(Direction.Translation.Back); if ((bits & STRAFE_LEFT) != 0) addTranslation(Direction.Translation.Left); if ((bits & STRAFE_RIGHT) != 0) addTranslation(Direction.Translation.Right); if ((bits & UP) != 0) addTranslation(Direction.Translation.Up); if ((bits & DOWN) != 0) addTranslation(Direction.Translation.Down); // Rotation ent.setYawPitchRoll(cmd.yaw, cmd.pitch, cmd.roll); /* if (Main.frame % 120 == 0) { System.out.println("rot for[" + ent.id + "]: " + String.format("YPR: %.0f, %.0f, %.0f", cmd.yaw, cmd.pitch, cmd.roll)); }*/ if (ent instanceof DynamicEntity) { DynamicEntity dynEnt = (DynamicEntity) ent; if ((bits & JUMP) != 0) { dynEnt.jump(); } if ((bits & SHOOT) != 0) { dynEnt.startShoot(); } else { dynEnt.stopShoot(); } } if (!transVec.isZero()) { if (ent.isFlyingEntity()) { ent.setRelativeDestination(transVec); } else { ent.setRelativeDestinationByYaw(transVec); } } else { ent.setDestination(null); } } updateQueue.clear(); } } public abstract void sendUpdateToClients(); private void addTranslation(Direction.Translation translation) { transVec.add(translation.vector); } public void update() { processDestroyedEntities(); processChatMessages(); maintainMonsterCount(); serverEventManager.process(); if (Main.isLocalServer()) { return; } processClientUpdates(); long currentTime = TimeUtils.millis(); long delta = currentTime - lastFrameTime; lastFrameTime = currentTime; accumulatedTime += delta; //Log.debug("server time delta: " + delta); while (accumulatedTime >= tickInterval) { tick(); accumulatedTime -= tickInterval; } } public void tick() { tickNum++; sendUpdateToClients(); processBulletHits(); } private static final float monsterCount = 8; private void maintainMonsterCount() { synchronized (Entity.list) { if (Entity.list.size < monsterCount) { serverEventManager.addEventToQueue(new ServerEvent.CreateMonster()); } } } protected final Array<Vector3> bulletHitQueue = new Array<>(); public void queueBulletHit(Vector3 loc) { synchronized (bulletHitQueue) { Vector3 bulletHitLoc = Pooler.v3(); bulletHitQueue.add(bulletHitLoc.set(loc)); } } public abstract void processBulletHits(); public void queueDestroyedEntity(int entityId) { synchronized (destroyedEntityQueue) { if (!destroyedEntityQueue.contains(entityId, false)) { destroyedEntityQueue.add(entityId); } } } public void processDestroyedEntities() { synchronized (destroyedEntityQueue) { for (int id : destroyedEntityQueue) { sendDestroyedEntityMessage(id); } destroyedEntityQueue.clear(); } } protected abstract void sendDestroyedEntityMessage(int id); public void queueChatMessage(ChatMessage chatMsg) { synchronized (chatQueue) { chatQueue.add(chatMsg); } } public abstract void processChatMessages(); public void sendPlayerConnectedChatMessage(int id) { ChatMessage chat = new ChatMessage("Player " + id + " has connected"); chat.playerId = -1; chat.createTime = TimeUtils.millis(); queueChatMessage(chat); } public void createMonster() { Log.debug("create monster"); DynamicEntity ent = DynamicEntity.createEntityNoId(Entity.EntityGraphicsType.Decal); Entity.assignEntityID(ent); ent.setPosition(MathUtils.random(20f, 100f), 50f, MathUtils.random(20f, 100f)); } public DynamicEntity createPlayer() { DynamicEntity playerEnt = DynamicEntity.createEntityNoId(Entity.EntityGraphicsType.Model); Entity.assignEntityID(playerEnt); playerEnt.setPlayer(new Player(playerEnt)); return playerEnt; } public void resetPlayerPosition(int id) { DynamicEntity playerEnt = (DynamicEntity) Entity.getEntityById(id); float x = MathUtils.random(0f, GameWorld.WORLD_WIDTH); float y = MathUtils.random(100f, 200f); float z = MathUtils.random(0f, GameWorld.WORLD_DEPTH); playerEnt.setPosition(x, y, z); } }