public synchronized void terminate() { // Must be synchronized because the attribute is accessed from different threads EventLog.log("evt.serverStopped", this.getClass(), this.getServerDesc()); startedAndRunning = false; this.interrupt(); // interrupt the server thread util.getConsole().removeServerThread(this); }
public synchronized void begin() { // Must be synchronized because the attribute is accessed from different threads if (enabled) { EventLog.log("evt.serverEnabled", this.getClass(), this.getServerDesc()); start(); util.getConsole().addServerThread(this); } }
/** * Set the new configuration after a reload. * * @param serverConfigurationCopy */ public synchronized void setNextConfiguration(SubnodeConfiguration serverConfigurationCopy) { // Must be synchronized because the attribute is accessed from different threads NDC.push(getServerDesc()); try { EventLog.log("evt.configReloadScheduled", this.getClass()); nextConfig = serverConfigurationCopy; } finally { NDC.pop(); } }
public List<Strategy> updateStrategies( String tagPath, SubnodeConfiguration config, Map<String, Strategy> strategies, Village village) { List<Strategy> deletableStrategies = new ArrayList<Strategy>(strategies.values()); List<Strategy> addedStrategies = new ArrayList<Strategy>(); List<Strategy> strategyList = new ArrayList<Strategy>(); List<SubnodeConfiguration> strategyConfigs = config.configurationsAt(tagPath); for (SubnodeConfiguration strategyConfig : strategyConfigs) { // Console.getInstance().checkFlags(); // This is wrong // Create a candidate strategy Strategy candidate = createStrategy(strategyConfig); // Check if it is already listed String idFromConfig = Strategy.getIdFromConfig(strategyConfig); Strategy oldStrategy = strategies.get(idFromConfig); if (oldStrategy != null) { if (addedStrategies.contains(candidate)) { // Strategy just added EventLog.log("evt.duplicateStrategy", this.getClass(), candidate.getId()); continue; } // Reconfigure existing oldStrategy.updateConfig(strategyConfig, util); deletableStrategies.remove(oldStrategy); strategyList.add(oldStrategy); } else { // Add new addedStrategies.add(candidate); candidate.init(strategyConfig, util, village); strategies.put(candidate.getId(), candidate); strategyList.add(candidate); } } // Delete disabled or removed strategies for (Strategy strategy : deletableStrategies) { strategy.setDeleted(true); strategies.remove(strategy.getId()); EventLog.log("evt.strategyRemoved", this.getClass(), strategy.getId()); } return strategyList; }
public void run() { EventLog.log("Starting nanny for \"" + getServerDesc() + "\""); setStartedAndRunning(true); NDC.push(getServerDesc()); try { // Nanny villages while (isKeepRunning()) { util.getConsole().checkPause(); TimeWhenRunnable firstTimeWhenRunnable = null; sharpTimeWhenRunnable = null; if ((messageMode != null) && !messageMode.equals("false")) { // check messages before for commands including resume int msgRead = messageReader.readMessages(util, loginUrl, messageMode); log.info("Messages Done next " + msgRead); } // check if still enabled if (!suspended) { // Loop all enabled villages EventLog.log("Checking Villages"); firstTimeWhenRunnable = processVillages(); log.info("Villages done"); // gac - move servers below villages // Loop all enabled server strategies TimeWhenRunnable serverStrategiesTimeWhenRunnable = processServerStrategies(); if (serverStrategiesTimeWhenRunnable.before(firstTimeWhenRunnable)) { log.debug("ServerStrategy Earlier than villages"); firstTimeWhenRunnable = serverStrategiesTimeWhenRunnable; } // gac add report processing - exists as server strategy to run periodically // but this will run at the end of every active period // count if outstanding after this read int moreRead = 0; if ((reportMode != null) && !reportMode.equals("false")) { // set the reporting mode and check reports // ReportMessageReader.getInstance().setReportsMode(reportMode); // EventLog.log(loginUrl+" about to read reportMode="+reportMode); moreRead = reportReader.readReports(util, loginUrl, reportMode); log.info("Server Reports done"); } // read messages again if ((messageMode != null) && !messageMode.equals("false")) { // check messages int msgRead = messageReader.readMessages(util, loginUrl, messageMode); log.info("Messages at end done next " + msgRead); // read more if consuming all message not just scanning titles for commands // if (!messageMode.equalsIgnoreCase("titles")) { if (!messageMode.equalsIgnoreCase(messageReader.getScanMode())) { moreRead += msgRead; } } if (moreRead != 0) { // still more to read firstTimeWhenRunnable = TimeWhenRunnable.NOW; // Re-run immediately log.debug("Villages will be run again because more reports to read (" + moreRead + ")"); } else { // no new reports left } if (isConfigurationChanged()) { // Update configuration updateConfig(getNextConfig()); EventLog.log("evt.configReloadDone", this.getClass()); this.nextConfig = null; // No need to synchronize here firstTimeWhenRunnable = TimeWhenRunnable.NOW; // Re-run immediately log.debug("Villages will be run again because of configuration update"); } } else { // suspended log.debug("All Strategies Suspended"); firstTimeWhenRunnable = new TimeWhenRunnable( System.currentTimeMillis() + SUSPENDCHECK * Util.MILLI_MINUTE); // try again later } boolean sharp = false; long milliPause = 0; long milliSharp = -1L; if (firstTimeWhenRunnable != null) { log.debug("Earliest Strategy @ " + firstTimeWhenRunnable); sharp = firstTimeWhenRunnable.isSharp(); milliPause = firstTimeWhenRunnable.getTime() - System.currentTimeMillis(); if (milliPause < 0) { milliPause = 0; } int pauseLimit = config.getInt("/@pauseLimit", 86400) * 1000; // Max 24h if (milliPause > pauseLimit) { log.info("Limiting pause from " + milliPause + " to " + pauseLimit); milliPause = pauseLimit; } if (sharpTimeWhenRunnable != null) { log.debug("Earliest sharp Strategy @ " + sharpTimeWhenRunnable); milliSharp = sharpTimeWhenRunnable.getTime() - System.currentTimeMillis(); if (milliSharp < 0) { milliSharp = 0; } } log.debug( "Next available Action in " + Util.milliToTimeString(milliPause) + (sharp ? " sharp: " + Util.milliToTimeString(milliSharp) : "")); } if (isKeepRunning()) { util.userPause(milliPause, sharp, milliSharp); util.shortestPause(sharp); // Just to be safe } } } catch (Exception e) { EventLog.log(e.getMessage()); log.error("", e); setEnabledAndStartStop(false); } finally { NDC.remove(); // not pop() } }
private void updateConfig(SubnodeConfiguration newServerConfig) throws InvalidConfigurationException { log.debug("Updating server config for " + getServerId()); this.config = newServerConfig; this.util.setServerConfig(newServerConfig); this.util.setServer(this); this.util.setServerId(user, getServerId()); // set server id for use in report processing // check if reading reports from overall server reportMode = newServerConfig.getString("/reports", null); // simple format to just read reportMode = newServerConfig.getString( "/reports/@enabled", reportMode); // complex config with processing if (reportMode != null) { SubnodeConfiguration reportConfig = newServerConfig.configurationAt("/reports"); reportMode = reportConfig.getString("/output/@format", reportMode); // complex config with processing EventLog.log(loginUrl + " reportMode=" + reportMode); reportReader.setReportsMode(reportMode, reportConfig); } messageMode = newServerConfig.getString("/messages", null); messageMode = newServerConfig.getString("/messages/@enabled", messageMode); // note once enabled have to turn off by setting false or will not call to disable if (messageMode != null) { SubnodeConfiguration messageConfig = newServerConfig.configurationAt("/messages"); String messageText = messageConfig.getString("/@commands", null); EventLog.log( loginUrl + " messageMode=" + messageMode + " Command Text (" + messageText + ")"); // use same as mode or a separate param? messageReader.setMessagesMode(messageMode, messageConfig); } setTribeType(newServerConfig); // // Update villages // When a village is disabled or removed from the configuration, it is deleted from // this.villages List<Village> deletableVillages = new ArrayList<Village>(villages.values()); // Loop all enabled villages List<SubnodeConfiguration> villageConfigs = newServerConfig.configurationsAt("/village[@enabled='true']"); for (SubnodeConfiguration villageConfig : villageConfigs) { String id = Village.getIdFromConfig(villageConfig); // Either uid or url Village village = this.villages.get(id); if (village == null) { // New village village = new Village(util, villageConfig, strategyStatus); this.villages.put(id, village); } else { // Village already exists village.updateConfig(villageConfig, util); deletableVillages.remove(village); // This village is still enabled } } // Removing deleted or disabled villages for (Village village : deletableVillages) { this.villages.remove(village.getId()); village.terminate(); } // // Update server strategies serverStrategyList = updateStrategies( "/serverStrategy[@enabled='true']", newServerConfig, serverStrategies, null); }
private TimeWhenRunnable processVillages() throws InvalidConfigurationException { TimeWhenRunnable firstTimeWhenRunnable = null; try { List<SubnodeConfiguration> villageConfigs = config.configurationsAt("/village[@enabled='true']"); for (SubnodeConfiguration villageConfig : villageConfigs) { // If configuration changed, abort current loop, update config and restart if (isConfigurationChanged()) { log.debug("Exiting village loop for configuration update"); break; } String id = Village.getIdFromConfig(villageConfig); Village village = villages.get(id); if (village == null) { log.warn( "Configuration file not aligned to village map; ignoring village " + villageConfig.getString("/@desc", "") + " " + villageConfig.getString("/url", "")); continue; } try { // getTimeWhenRunnable may update to check for triggering resources so can change page TimeWhenRunnable currentTimeWhenRunnable = village.getTimeWhenRunnable(); // log.debug("currentTimeWhenRunnnable:"+currentTimeWhenRunnable); if (currentTimeWhenRunnable.before(new Date())) { EventLog.log("Processing village \"" + village.getDesc() + "\""); village.execute(); // Execute strategies currentTimeWhenRunnable = village.getTimeWhenRunnable(); // log.debug("currentTimeWhenRunnnable after execute:"+currentTimeWhenRunnable); } else { EventLog.log( "Village \"" + village.getDesc() + "\" sleeping until " + currentTimeWhenRunnable); } // use current here and only reread after execute, then use new below if ((firstTimeWhenRunnable == null) || firstTimeWhenRunnable.after(currentTimeWhenRunnable)) { log.trace("Earlier Village " + village.getDesc() + " " + currentTimeWhenRunnable); firstTimeWhenRunnable = currentTimeWhenRunnable; } // check for earliest sharp strategy in village, return NEVER if none- if is store // earliest sharp one TimeWhenRunnable newTimeWhenRunnable = village.getTimeWhenSharp(); if (newTimeWhenRunnable.isSharp()) { if (sharpTimeWhenRunnable == null || sharpTimeWhenRunnable.after(newTimeWhenRunnable)) { // log.trace("Earlier Village "+village.getDesc()+" "+newTimeWhenRunnable); sharpTimeWhenRunnable = newTimeWhenRunnable; log.trace( "Earlier sharp Village is " + village.getDesc() + " " + sharpTimeWhenRunnable); } } } catch (SkipVillageRequested e) { log.debug("Village skipped"); continue; } catch (SkipRequested e) { // Just keep going log.debug("Action skipped"); } catch (ServerFatalException e) { throw e; // Will catch below } catch (Exception e) { String s = "Village \"" + village.getDesc() + "\" error (retrying later): "; log.error(s, e); EventLog.log(s + e.getMessage()); util.shortestPause(false); // Just to be safe firstTimeWhenRunnable = null; // Retry after userPause } } } catch (ConcurrentModificationException e) { // This shouldn't happen anymore // Just ignore log.debug( "Village list was modified while server running (ConcurrentModificationException): skipping and repeating"); } // log.debug("returning firstTimeWhenRunnable:"+firstTimeWhenRunnable); return firstTimeWhenRunnable; }
private TimeWhenRunnable processServerStrategies() { TimeWhenRunnable firstTimeWhenRunnable = TimeWhenRunnable.NEVER; // Run through each strategy for (Strategy strategy : this.serverStrategyList) { if (strategy.isDeleted() || util.getConsole().isQuitting()) { continue; } NDC.push("(" + strategy.getDesc() + ")"); try { util.getConsole().checkFlags(); TimeWhenRunnable timeWhenRunnable = strategy.getTimeWhenRunnable(); if (timeWhenRunnable == null || timeWhenRunnable.isOpportunist() || timeWhenRunnable.before(new Date())) { EventLog.log("Executing strategy \"" + strategy.getDesc() + "\""); TimeWhenRunnable nextTime = strategy.execute(); if (nextTime == null) { // Just to be safe EventLog.log("Timewhenrunnable = null returned"); log.warn("(Internal Error) Shouldn't return null"); nextTime = new TimeWhenRunnable(System.currentTimeMillis()); } log.debug( String.format( "Server Strategy %s will be run after %s ", strategy.getDesc(), nextTime.getTimeWhenRunnable())); strategy.setTimeWhenRunnable(nextTime); if (firstTimeWhenRunnable.after(nextTime)) { firstTimeWhenRunnable = nextTime; log.trace( "Earliest Server Strategy is " + strategy.getDesc() + " " + firstTimeWhenRunnable); } // check if this one is sharp - if so store earliest sharp one /* if (timeWhenRunnable!=null && timeWhenRunnable.isSharp()) { if (sharpTimeWhenRunnable == null || sharpTimeWhenRunnable.after(timeWhenRunnable)) { // log.trace("Earliest Village "+village.getDesc()+" "+newTimeWhenRunnable); sharpTimeWhenRunnable = timeWhenRunnable; log.debug("Earliest sharp Strategy is "+strategy.getDesc()+" "+timeWhenRunnable); } } */ if (nextTime.isSharp()) { if (sharpTimeWhenRunnable == null || sharpTimeWhenRunnable.after(nextTime)) { // log.trace("Earliest Village "+village.getDesc()+" "+newTimeWhenRunnable); sharpTimeWhenRunnable = nextTime; log.trace( "Earliest sharp Strategy is " + strategy.getDesc() + " " + sharpTimeWhenRunnable); } } } } catch (ConversationException e) { String s = "Error while executing strategy \"" + strategy.getDesc() + "\" (skipping)"; Util.log(s, e); EventLog.log(s); util.shortestPause(false); // Just to be safe // Just keep going to the next strategy } catch (TranslationException e) { EventLog.log(e.getMessage()); log.error("Translation error", e); // Keep going } catch (Exception e) { // Any other exception skips the strategy but keeps the server going // so that bugs in one strategy don't prevent other strategies from executing String message = EventLog.log("msg.strategyException", this.getClass(), strategy.toString()); log.error(message, e); util.shortestPause(false); // Just to be safe firstTimeWhenRunnable = TimeWhenRunnable.NOW; // Retry after userPause } finally { NDC.pop(); } } return firstTimeWhenRunnable; }
public TimeWhenRunnable execute() throws ConversationException { // <strategy class="Farmizzator" desc="DESCRIPTION" enabled="true"> // <target x="XCOORD" y="YCOORD" village="TARGET_VILLAGE" movement="REINFORCE|RAID|ATTACK" // spy="RESOURCES|DEFENSES" rate="RATE" item="CATA_TARGET1, CATA_TARGET2"/> // <troops type="TROOP_NAME" allowLess="ALLOWLESS" min="MINIMUM_TROOPS" randomise="RANDOMISE" // enabled="true">TROOP_AMOUNT</troops> // <minPauseMinutes>MIN_PAUSE</minPauseMinutes> // </strategy> log.info("Executing strategy " + super.getDesc()); NDC.push(super.getDesc()); // TODO Per le volte successive: // TODO Controlla il report dell'attacco (difficile!) // TODO Se sono state perse troppe truppe, cancella questa strategy try { // EventLog.log("checking reports for 92,-95 d=396988&c=50 = " + // util.getMapIdFromPosition("92","-95")); // ReportMessageReader.getInstance().getAverageBounty(util.getMapIdFromPosition("92","-95")); // EventLog.log("Bounty // "+ReportMessageReader.getInstance().getLastBounty(util.getServerId(),util.getMapIdFromPosition("92","-95"))); // EventLog.log("Average Bounty "+ReportMessageReader.getInstance().getAverageBounty(util, // "92","-95")); // EventLog.log("Last Bounty // "+ReportMessageReader.getInstance().getLastBounty(util.getServerId(),util.getMapIdFromPosition("92","-95"))); // System.exit(0); RallyPoint rallyPoint = village.getRallyPoint(); if (rallyPoint == null) { log.debug("No rally point"); int minPauseMinutes = super.config.getInt("/minPauseMinutes", 25); minPauseMinutes = super.config.getInt( "/@minPauseMinutes", minPauseMinutes); // support on strategy line as well return new TimeWhenRunnable( System.currentTimeMillis() + minPauseMinutes * Util.MILLI_MINUTE); // Try again later } TroopTypeMap availablePerType = rallyPoint.fetchSendableTroops(super.util, false); TroopTypeMap toSend = new TroopTypeMap(); boolean sendable = true; int totTroops = 0; List<SubnodeConfiguration> troopsNodes = super.config.configurationsAt("/troops"); for (SubnodeConfiguration troopsNode : troopsNodes) { boolean enabled = troopsNode.getBoolean("/@enabled", true); // Enabled by default if (!enabled) { continue; } String type = troopsNode.getString("/@type", null); if (type == null) { log.error("Missing \"type\" attribute in strategy \"" + super.getDesc() + "\""); continue; } String fullkey = util.getTranslator().getKeyword(type); // romans.troop1 String typeKey = fullkey.substring(fullkey.indexOf(".") + 1); TroopType troopType = TroopType.fromString(typeKey); int val = troopsNode.getInt("/", 0); boolean allowLess = troopsNode.getBoolean("/@allowLess", false); Integer available = availablePerType.get(troopType); // Check if we have enough troops if (val > available && !allowLess) { sendable = false; break; } // Check if we can send at least min troops String minimum = troopsNode.getString("/@min", "0"); int min = 0; boolean percent = false; if (minimum.endsWith("%")) { percent = true; minimum = minimum.replace("%", ""); } try { min = Integer.parseInt(minimum); } catch (NumberFormatException e) { throw new FatalException( String.format("Invalid numeric value for %s: \"%s\"", type, minimum)); } if (percent) { min = (val * min) / 100; } if (available < min) { sendable = false; break; } // Randomise // Accept both "randomise" and "randomize", with "true" as default boolean randomise = troopsNode.getBoolean("/@randomise", troopsNode.getBoolean("/@randomize", true)); if (randomise) { int maxDelta = (int) (val * RANDOMISE_RATIO); if (!allowLess) { // value can't be less than what specified val = val + random.nextInt(maxDelta + 1); } else { // value can be +- randVal val = val - maxDelta + random.nextInt(2 * maxDelta + 1); } } // Add troops to send val = val > available ? available : val; // Upper limit val = val < min ? min : val; // Lower limit toSend.put(troopType, val); totTroops += val; } int minPauseMinutes = super.config.getInt("/minPauseMinutes", 5); minPauseMinutes = super.config.getInt( "/@minPauseMinutes", minPauseMinutes); // support on strategy line as well if (sendable == false || totTroops == 0) { EventLog.log("Not enough troops"); return new TimeWhenRunnable( System.currentTimeMillis() + minPauseMinutes * Util.MILLI_MINUTE); // Try again later } Coordinates coord = new Coordinates(config, "target"); // Handles "travian style" coords String x = coord.getX(); String y = coord.getY(); String village = super.config.getString("/target/@village", ""); String movement = super.config.getString("/target/@movement", "attack"); String fullkey = "movement." + translator.getKeyword(movement, "movement"); // movement.attack TroopTransferType transferType = TroopTransferType.fromKey(fullkey); String itemString1 = super.config.getString("/target/@item[1]", null); String itemString2 = super.config.getString("/target/@item[2]", null); TroopTransferType spyType = null; String spyTypeString = super.config.getString("/target/@spy", null); if (spyTypeString != null) { fullkey = "movement." + translator.getKeyword(spyTypeString, "movement"); // movement.spy.resources spyType = TroopTransferType.fromKey(fullkey); } // check for previous bounty if not spying else if ((!x.equals("")) && (!y.equals(""))) { // log.debug("checking reports for " + x + "," + y + " id " + // util.getMapIdFromPosition(x,y)); // ReportMessageReader.getInstance().getAverageBounty(util.getServerId(),util.getMapIdFromPosition(x,y)); // log.debug("Average Bounty " + ReportMessageReader.getInstance().getAverageBounty(util, x, // y)); Valley v = new Valley(util, x, y); log.debug("Average Bounty " + v.getAverageBounty()); // EventLog.log("Last Bounty // "+ReportMessageReader.getInstance().getLastBounty(util.getServerId(),util.getMapIdFromPosition( x, y))); } super.village .gotoMainPage(); // Ensure you do it from the right village (someone might have clicked to // a different village meanwhile) int secondsToArrive; try { secondsToArrive = rallyPoint.sendTroops( util, x, y, village, toSend, transferType, new String[] {itemString1, itemString2}, spyType); totErrors = 0; } catch (ConversationException e) { log.error(e); totErrors++; if (totErrors <= MAX_ERRORS) { EventLog.log( String.format( "Strategy has error; retrying later (%s left)", MAX_ERRORS - totErrors)); util.shortestPause(false); // Just to be safe return new TimeWhenRunnable( System.currentTimeMillis() + minPauseMinutes * Util.MILLI_MINUTE); // Try again later } else { log.error("Strategy has too many errors; disabling", e); EventLog.log("Strategy has too many errors; disabling"); return TimeWhenRunnable.NEVER; } } if (secondsToArrive > 0) { log.info(String.format("Strategy %s done for now", getDesc())); double rate = Math.max(super.config.getDouble("/target/@rate", 1), Double.MIN_VALUE); double raidTime = (2 * secondsToArrive * Util.MILLI_SECOND); long waitMillis = (long) (raidTime / rate); long timeTemp = waitMillis / 60000; EventLog.log( String.format( "Strategy %s done for now: Next run in %s minutes at rate %s", getDesc(), Long.toString(timeTemp), Double.toString(rate))); long MyTimeToRun = System.currentTimeMillis() + waitMillis; return new TimeWhenRunnable(MyTimeToRun); } else { log.error("Error in Farmizzator"); EventLog.log(String.format("Strategy %s disabled on error", getDesc())); return new TimeWhenRunnable(false); } } finally { NDC.pop(); } }