diff --git a/src/discord/MagicBot.java b/src/discord/MagicBot.java index 8744e3e4..8be071ee 100644 --- a/src/discord/MagicBot.java +++ b/src/discord/MagicBot.java @@ -217,6 +217,9 @@ public class MagicBot extends ListenerAdapter { case "#server": ServerRequestHandler.handleRequest(event, args); break; + case "#dev": + DevRequestHandler.handleRequest(event, args); + break; case "#logs": LogsRequestHandler.handleRequest(event, args); break; @@ -302,7 +305,8 @@ public class MagicBot extends ListenerAdapter { "#setavail true/false Toggle status of database access.\n" + "#server reboot/shutdown are your options.\n" + "#logs magicbot/world/login n (tail)\n" + - "#flash send flash message\n" + + "#flash Send flash message\n" + + "#dev help (list dev subcommands)\n" + "#trash /detail/flush"; sendResponse(event, helpString); } diff --git a/src/discord/handlers/DevRequestHandler.java b/src/discord/handlers/DevRequestHandler.java new file mode 100644 index 00000000..a9b741d1 --- /dev/null +++ b/src/discord/handlers/DevRequestHandler.java @@ -0,0 +1,106 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package discord.handlers; + +import discord.MagicBot; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import org.pmw.tinylog.Logger; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.stream.Collectors; + +public class DevRequestHandler { + + public static void handleRequest(MessageReceivedEvent event, String[] args) { + + String serverCommand; + String commandArgument = ""; + String commandString = ""; + String logString = ""; + + ProcessBuilder processBuilder; + + // Early exit if database unavailable or is not an admin + + if (MagicBot.isAdminEvent(event) == false) + return; + + serverCommand = args[0].toLowerCase().trim(); + + if (args.length == 2) + commandArgument = args[1].toLowerCase().trim(); + + switch (serverCommand) { + + case "build" : + commandString = "./mbdevbuild.sh"; + break; + case "restart": + commandString = "./mbdevrestart.sh"; + break; + case "debug": + commandString = "./mbdevdebug.sh"; + break; + case "shutdown": + commandString = "./mbdevkill.sh"; + break; + case "lastout": + MagicBot.sendResponse(event, getLastOutput()); + return; + case "console": + commandString = "./mbdevconsole.sh"; + break; + case "help": + MagicBot.sendResponse(event, + "#dev build (blank==master) \n" + + "#dev shutdown (Shutdown dev server)\n" + + "#dev restart (Restarts the server)\n"+ + "#dev debug (Restarts server in debug mode)\n" + + "#dev console # (Displays # lines from console)\n" + + "#dev lastout (Displays output from last command) \n"); + return; + default: + break; + } + + if (commandString.isEmpty()) { + MagicBot.sendResponse(event, "Unrecognized Dev command: " + serverCommand + " " + commandArgument); + return; + } + + processBuilder = new ProcessBuilder("/bin/sh", "-c", commandString + " " + commandArgument + " > devLastOut"); + logString = String.join(" ",processBuilder.command().toArray(new String[0])); + + try { + processBuilder.start(); + } catch (IOException e) { + Logger.info(e.toString()); + } + + MagicBot.sendResponse(event, "Executed on dev: " + logString + "\n" + + "Use #dev lastout to view results"); + + } + private static String getLastOutput() { + + String outString = null; + try { + outString = Files.lines(Paths.get("devLastOut")) + .collect(Collectors.joining(System.lineSeparator())); + } catch (IOException e) { + e.printStackTrace(); + } + return outString; + } +} \ No newline at end of file diff --git a/src/engine/db/handlers/dbGuildHandler.java b/src/engine/db/handlers/dbGuildHandler.java index 43d43593..b21ad7d1 100644 --- a/src/engine/db/handlers/dbGuildHandler.java +++ b/src/engine/db/handlers/dbGuildHandler.java @@ -19,6 +19,7 @@ import org.pmw.tinylog.Logger; import java.sql.ResultSet; import java.sql.SQLException; +import java.time.LocalDateTime; import java.util.ArrayList; public class dbGuildHandler extends dbHandlerBase { @@ -114,8 +115,12 @@ public class dbGuildHandler extends dbHandlerBase { return outputStr; } - - + public boolean SET_LAST_WOO_UPDATE(Guild guild, LocalDateTime lastEditTime) { + prepareCallable("UPDATE `obj_guild` SET `lastWooEditTime`=? WHERE `UID`=?"); + setLocalDateTime(1, lastEditTime); + setLong(2, (long) guild.getObjectUUID()); + return (executeUpdate() > 0); + } public ArrayList GET_GUILD_ALLIES(final int id) { prepareCallable("SELECT g.* FROM `obj_guild` g, `dyn_guild_allianceenemylist` l " diff --git a/src/engine/db/handlers/dbMineHandler.java b/src/engine/db/handlers/dbMineHandler.java index acc8384b..43753937 100644 --- a/src/engine/db/handlers/dbMineHandler.java +++ b/src/engine/db/handlers/dbMineHandler.java @@ -6,19 +6,6 @@ // Magicbane Emulator Project © 2013 - 2022 // www.magicbane.com - - - - -// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . -// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· -// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ -// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ -// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ -// Magicbane Emulator Project © 2013 - 2022 -// www.magicbane.com - - package engine.db.handlers; import engine.Enum; diff --git a/src/engine/net/client/ClientMessagePump.java b/src/engine/net/client/ClientMessagePump.java index 07ede49c..836ac36e 100644 --- a/src/engine/net/client/ClientMessagePump.java +++ b/src/engine/net/client/ClientMessagePump.java @@ -265,9 +265,6 @@ public class ClientMessagePump implements NetMsgHandler { case ARCMINEWINDOWAVAILABLETIME: MineWindowAvailableTime((ArcMineWindowAvailableTimeMsg) msg, origin); break; - case ARCMINEWINDOWCHANGE: - MineWindowChange((ArcMineWindowChangeMsg) msg, origin); - break; case ARCOWNEDMINESLIST: ListOwnedMines((ArcOwnedMinesListMsg) msg, origin); break; @@ -1746,10 +1743,6 @@ boolean updateCity = false; cost = sell.getBaseValue(); - - if (sell.isID()) - cost = sell.getMagicValue(); - float bargain = player.getBargain(); float profit = npc.getBuyPercent(player) + bargain; diff --git a/src/engine/net/client/Protocol.java b/src/engine/net/client/Protocol.java index d95becd3..38965bb8 100644 --- a/src/engine/net/client/Protocol.java +++ b/src/engine/net/client/Protocol.java @@ -36,7 +36,7 @@ public enum Protocol { ARCMINECHANGEPRODUCTION(0x1EAA993F, ArcMineChangeProductionMsg.class, null), ARCMINETOWERCRESTUPDATE(0x34164D0D, null, null), ARCMINEWINDOWAVAILABLETIME(0x6C909DE7, ArcMineWindowAvailableTimeMsg.class, null), - ARCMINEWINDOWCHANGE(0x92B2148A, ArcMineWindowChangeMsg.class, null), + ARCMINEWINDOWCHANGE(0x92B2148A, ArcMineWindowChangeMsg.class, MineWindowChangeHandler.class), ARCOWNEDMINESLIST(0x59184455, ArcOwnedMinesListMsg.class, null), ARCPETATTACK(0x18CD61AD, PetAttackMsg.class, null), // Pet Attack ARCPETCMD(0x4E80E001, PetCmdMsg.class, null), // Stop ArcPetAttack, Toggle Assist, Toggle Rest diff --git a/src/engine/net/client/handlers/MineWindowChangeHandler.java b/src/engine/net/client/handlers/MineWindowChangeHandler.java new file mode 100644 index 00000000..96312545 --- /dev/null +++ b/src/engine/net/client/handlers/MineWindowChangeHandler.java @@ -0,0 +1,115 @@ +// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . +// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· +// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ +// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ +// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ +// Magicbane Emulator Project © 2013 - 2022 +// www.magicbane.com + +package engine.net.client.handlers; + +import engine.Enum; +import engine.Enum.DispatchChannel; +import engine.exception.MsgSendException; +import engine.gameManager.BuildingManager; +import engine.gameManager.ChatManager; +import engine.gameManager.DbManager; +import engine.gameManager.SessionManager; +import engine.net.Dispatch; +import engine.net.DispatchMessage; +import engine.net.client.ClientConnection; +import engine.net.client.msg.ArcMineWindowChangeMsg; +import engine.net.client.msg.ClientNetMsg; +import engine.net.client.msg.ErrorPopupMsg; +import engine.net.client.msg.KeepAliveServerClientMsg; +import engine.objects.Building; +import engine.objects.Guild; +import engine.objects.GuildStatusController; +import engine.objects.PlayerCharacter; +import engine.server.MBServerStatics; +import org.pmw.tinylog.Logger; + +import java.time.LocalDateTime; + +/* + * @Author: + * @Summary: Processes requests to change a mine's opendate + */ + +public class MineWindowChangeHandler extends AbstractClientMsgHandler { + + public MineWindowChangeHandler() { + super(ArcMineWindowChangeMsg.class); + } + + @Override + protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException { + + PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin); + ArcMineWindowChangeMsg mineWindowChangeMsg = (ArcMineWindowChangeMsg)baseMsg; + int newMineTime; + + if (playerCharacter == null) + return true; + + Building treeOfLife = BuildingManager.getBuildingFromCache(mineWindowChangeMsg.getBuildingID()); + + if (treeOfLife == null) + return true; + + if (treeOfLife.getBlueprintUUID() == 0) + return true; + + if (treeOfLife.getBlueprint().getBuildingGroup() != Enum.BuildingGroup.TOL) + return true; + + Guild mineGuild = treeOfLife.getGuild(); + if (mineGuild == null) + return true; + + if (!Guild.sameGuild(mineGuild, playerCharacter.getGuild())) + return true; //must be same guild + + if (GuildStatusController.isInnerCouncil(playerCharacter.getGuildStatus()) == false) // is this only GL? + return true; + + newMineTime = mineWindowChangeMsg.getTime(); + + // Enforce 15hr restriction between WOO edits + + if (LocalDateTime.now().isBefore(mineGuild.lastWooEditTime.plusHours(14))) { + ErrorPopupMsg.sendErrorMsg(playerCharacter, "You must wait 15 hours between WOO changes."); + return true; + } + + //hodge podge sanity check to make sure they don't set it before early window and is not set at late window. + + if (newMineTime < MBServerStatics.MINE_EARLY_WINDOW && + newMineTime != MBServerStatics.MINE_LATE_WINDOW) + return true; //invalid mine time, must be in range + + // Update guild mine time + + if (!DbManager.GuildQueries.UPDATE_MINETIME(mineGuild.getObjectUUID(), newMineTime)) { + Logger.error("MineWindowChange", "Failed to update mine time for guild " + mineGuild.getObjectUUID()); + ChatManager.chatGuildError(playerCharacter, "Failed to update the mine time"); + return true; + } + + mineGuild.setMineTime(newMineTime); + mineGuild.lastWooEditTime = LocalDateTime.now(); + + // Update guild WOO timer for reboot persistence + + if (!DbManager.GuildQueries.SET_LAST_WOO_UPDATE(mineGuild, mineGuild.lastWooEditTime)) { + Logger.error("MineWindowChange", "Failed to update woo timer for guild " + mineGuild.getObjectUUID()); + ChatManager.chatGuildError(playerCharacter, "A Serious error has for to occurred."); + return true; + } + + ChatManager.chatGuildInfo(playerCharacter, "Mine time updated."); + + return true; + } + +} \ No newline at end of file diff --git a/src/engine/objects/Guild.java b/src/engine/objects/Guild.java index e28009af..a76f151e 100644 --- a/src/engine/objects/Guild.java +++ b/src/engine/objects/Guild.java @@ -40,7 +40,10 @@ import org.pmw.tinylog.Logger; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; @@ -79,6 +82,7 @@ public class Guild extends AbstractWorldObject { private String hash; private boolean ownerIsNPC; + public LocalDateTime lastWooEditTime; public HashMap guildAlliances = new HashMap<>(); /** @@ -176,8 +180,13 @@ public class Guild extends AbstractWorldObject { this.teleportMax = rs.getInt("teleportMax"); this.mineTime = rs.getInt("mineTime"); - this.hash = rs.getString("hash"); + Timestamp lastWooRequest = rs.getTimestamp("lastWooEditTime"); + + if (lastWooRequest != null) + this.lastWooEditTime = lastWooRequest.toLocalDateTime(); + + this.hash = rs.getString("hash"); } public void setNation(Guild nation) { @@ -565,11 +574,10 @@ public class Guild extends AbstractWorldObject { if (this.equals(toSub)) return false; - - switch(this.guildState){ - case Nation: - case Sovereign: - canSub = true; + switch(this.guildState) { + case Nation: + case Sovereign: + canSub = true; break; default: canSub = false; @@ -583,7 +591,10 @@ public class Guild extends AbstractWorldObject { default: canSub = false; } - + City nationCap = City.getCity(nation.cityUUID); + if (nation.getSubGuildList().size() >= nationCap.getRank()) { + canSub = false; + } return canSub; } diff --git a/src/engine/objects/Mine.java b/src/engine/objects/Mine.java index e53c6d86..39564bd2 100644 --- a/src/engine/objects/Mine.java +++ b/src/engine/objects/Mine.java @@ -35,6 +35,7 @@ import org.pmw.tinylog.Logger; import java.net.UnknownHostException; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Timestamp; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.concurrent.ConcurrentHashMap; @@ -141,48 +142,17 @@ public class Mine extends AbstractGameObject { this.lastClaimerID = 0; this.lastClaimerSessionID = null; - java.sql.Timestamp mineTimeStamp = rs.getTimestamp("mine_openDate"); - - - Building building = BuildingManager.getBuildingFromCache(this.buildingID); - - if (mineTimeStamp == null && (this.owningGuild == null || this.owningGuild.isErrant() || building.getRank() < 1)){ - if (building != null){ - String zoneName = building.getParentZone().getName(); - String parentZoneName = building.getParentZone().getParent().getName(); - Logger.info(zoneName + " in " + parentZoneName + " has a dirty mine, setting active."); - } - this.dirtyMine = true; - openDate = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); - return; - }else if (this.owningGuild.isErrant() || nation.isErrant()){ - openDate = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); - return; - }else if (mineTimeStamp == null){ - - this.openDate = LocalDateTime.now().withHour(mineTime).withMinute(0).withSecond(0).withNano(0); - - if (LocalDateTime.now().isAfter(this.openDate.plusHours(1))) - this.openDate = this.openDate.plusDays(1); - return; - }else{ - this.openDate = mineTimeStamp.toLocalDateTime().withHour(mineTime); - - if (LocalDateTime.now().isAfter(this.openDate.plusHours(1))){ - this.openDate = this.openDate.plusDays(1); - return; - } - } - - //after 1 day... - if(this.openDate.getDayOfYear() - LocalDateTime.now().getDayOfYear() > 1){ - this.openDate = this.openDate.withDayOfYear(LocalDateTime.now().getDayOfYear()); + Timestamp mineOpenDateTime = rs.getTimestamp("mine_openDate"); + + if (mineOpenDateTime != null) + this.openDate = mineOpenDateTime.toLocalDateTime(); + + // Set opendate to tomorrow if window has passed + if (LocalDateTime.now().isAfter(this.openDate.plusHours(1))) - this.openDate = this.openDate.plusDays(1); - return; - } - + this.openDate = LocalDateTime.now().plusDays(1).withHour(this.openDate.getHour()).withMinute(0).withSecond(0).withNano(0); + } public static void SendMineAttackMessage(Building mine){ @@ -258,10 +228,6 @@ try{ */ private void initializeMineTime(){ - //Mine time has already been set at loading from the database. skip. - - if (this.openDate != null) - return; Guild nation = null; @@ -270,7 +236,7 @@ try{ int mineTime = (nation != null && !nation.isErrant()) ? nation.getMineTime() : MBServerStatics.MINE_EARLY_WINDOW; - LocalDateTime openDate = LocalDateTime.now().withHour(mineTime).withMinute(0).withSecond(0).withNano(0); + this.openDate = this.openDate.withHour(mineTime).withMinute(0).withSecond(0).withNano(0); //Failed to Update Database, default mine time. @@ -281,8 +247,6 @@ try{ return; } - this.openDate = openDate; - } public boolean changeProductionType(Resource resource){ @@ -560,6 +524,11 @@ try{ public boolean handleEndMineWindow(){ + // No need to end the window of a mine which never opened. + + if (this.isActive == false) + return false; + Building mineBuilding = BuildingManager.getBuildingFromCache(this.buildingID); if (mineBuilding == null){