private void buildPerceptions() { playersToRemove.clear(); /** We reset the cache at Perceptions */ MessageS2CPerception.clearPrecomputedPerception(); for (PlayerEntry entry : playerContainer) { try { // Before creating the perception we check the player is still there. if (entry.isTimeout()) { logger.info("Request (TIMEOUT) disconnection of Player " + entry.getAddress()); playersToRemove.add(entry); continue; } if (entry.state == ClientState.GAME_BEGIN) { Perception perception = getPlayerPerception(entry); sendPlayerPerception(entry, perception, entry.object); } } catch (Exception e) { logger.error( "Removing player(" + entry.clientid + ") because it caused a Exception while contacting it", e); playersToRemove.add(entry); } } for (PlayerEntry entry : playersToRemove) { logger.warn("RP Disconnecting entry: " + entry); netMan.disconnectClient(entry.channel); } }
/** * Constructor * * @param netMan the NetworkServerManager so that we can send message * @throws Exception in case of an unexpected error */ public RPServerManager(INetworkServerManager netMan) throws Exception { super("RPServerManager"); try { stats = Statistics.getStatistics(); keepRunning = true; isfinished = false; scheduler = new RPScheduler(); contentsToTransfer = new HashMap<RPObject, List<TransferContent>>(); playerContainer = PlayerEntryContainer.getContainer(); playersToRemove = new LinkedList<PlayerEntry>(); this.netMan = netMan; Configuration conf = Configuration.getConfiguration(); /* * This method loads the extensions that implement the game server * code. */ initializeExtensions(conf); String duration = conf.get("turn_length"); turnDuration = Long.parseLong(duration); turn = 0; } catch (Exception e) { logger.warn("ABORT: Unable to create RPZone, RPRuleProcessor or RPAIManager instances", e); throw e; } }
/** * Takes the given RPObject as the new RPObject representing the user's character. Does nothing if * the provided RPObject isn't the user's character object as determined by * isCharacter(newCharacter) or the provided RPObject is the same as the current one. * * @param newCharacter the new RPObject representing the users character */ public void setCharacter(final RPObject newCharacter) { if (isCharacter(newCharacter) && this.myCharacter != newCharacter) { myCharacter = newCharacter; name = newCharacter.get("name"); } else { log.warn("Character not set because it's not our character."); } }
private void deliverTransferContent() { synchronized (contentsToTransfer) { for (Map.Entry<RPObject, List<TransferContent>> val : contentsToTransfer.entrySet()) { RPObject target = val.getKey(); List<TransferContent> content = val.getValue(); PlayerEntry entry = playerContainer.get(target); if (entry == null) { logger.warn( "Entry for player (" + target + ") does not exist: " + playerContainer, new Throwable()); continue; } if (content == null) { logger.warn("content is null"); } if (!entry.contentToTransfer.isEmpty()) { // prevent DoS if the client never confirms the Transfer offer if (entry.contentToTransfer.size() > 30) { synchronized (entry.contentToTransfer) { for (int i = 0; i < 10; i++) { entry.contentToTransfer.remove(0); } } } logger.warn( "Adding to existing contentToTransfer for player " + entry.character + " old: " + entry.contentToTransfer + " added " + content); } entry.contentToTransfer.addAll(content); MessageS2CTransferREQ mes = new MessageS2CTransferREQ(entry.channel, content); mes.setClientID(entry.clientid); mes.setProtocolVersion(entry.getProtocolVersion()); netMan.sendMessage(mes); } contentsToTransfer.clear(); } }
/** * This method disconnects a player from the server. * * @param object the player object that we want to disconnect from world */ public void disconnectPlayer(RPObject object) { PlayerEntry entry = playerContainer.get(object); if (entry == null) { /* * There is no player entry for such channel This is not necesaryly * an error, as the connection could be anything else but an arianne * client or we are just disconnecting a player that logout * correctly. */ logger.warn("There is no PlayerEntry associated to this RPObject."); return; } netMan.disconnectClient(entry.channel); }
@Override public void run() { try { long start = System.nanoTime(); long stop; long delay; long timeStart = 0; long[] timeEnds = new long[12]; while (keepRunning) { stop = System.nanoTime(); logger.debug("Turn time elapsed: " + ((stop - start) / 1000) + " microsecs"); delay = turnDuration - ((stop - start) / 1000000); if (delay < 0) { StringBuilder sb = new StringBuilder(); for (long timeEnd : timeEnds) { sb.append(" " + (timeEnd - timeStart)); } logger.warn("Turn duration overflow by " + (-delay) + " ms: " + sb.toString()); } else if (delay > turnDuration) { logger.error( "Delay bigger than Turn duration. [delay: " + delay + "] [turnDuration:" + turnDuration + "]"); delay = 0; } // only sleep when the turn delay is > 0 if (delay > 0) { try { Thread.sleep(delay); } catch (InterruptedException e) { // ignore } } start = System.nanoTime(); timeStart = System.currentTimeMillis(); playerContainer.getLock().requestWriteLock(); try { timeEnds[0] = System.currentTimeMillis(); /* Get actions that players send */ scheduler.nextTurn(); timeEnds[1] = System.currentTimeMillis(); /* Execute them all */ scheduler.visit(ruleProcessor); timeEnds[2] = System.currentTimeMillis(); /* Compute game RP rules to move to the next turn */ ruleProcessor.endTurn(); timeEnds[3] = System.currentTimeMillis(); /* Send content that is waiting to players */ deliverTransferContent(); timeEnds[4] = System.currentTimeMillis(); /* Tell player what happened */ buildPerceptions(); timeEnds[5] = System.currentTimeMillis(); /* save players regularly to the db */ savePlayersPeriodicly(); timeEnds[6] = System.currentTimeMillis(); /* Move zone to the next turn */ world.nextTurn(); timeEnds[7] = System.currentTimeMillis(); turn++; ruleProcessor.beginTurn(); timeEnds[8] = System.currentTimeMillis(); } finally { playerContainer.getLock().releaseLock(); timeEnds[9] = System.currentTimeMillis(); } try { stats.set("Objects now", world.size()); } catch (ConcurrentModificationException e) { // TODO: size is obviously not threadsafe as it asks the underlying zone.objects for its // sizes, which are not threadsafe. } timeEnds[10] = System.currentTimeMillis(); TransactionPool.get().kickHangingTransactionsOfThisThread(); timeEnds[11] = System.currentTimeMillis(); } } catch (Throwable e) { logger.error("Unhandled exception, server will shut down.", e); } finally { isfinished = true; } }