diff --git a/src/intents/ServerManagementIntent.java b/src/intents/ServerManagementIntent.java index 86e75028..9a12c0e4 100644 --- a/src/intents/ServerManagementIntent.java +++ b/src/intents/ServerManagementIntent.java @@ -27,6 +27,8 @@ ***********************************************************************************/ package intents; +import java.util.concurrent.TimeUnit; + import resources.control.Intent; import resources.player.Player; @@ -37,6 +39,7 @@ public class ServerManagementIntent extends Intent { private String target; private ServerManagementEvent event; private long time; + private TimeUnit timeUnit; public ServerManagementIntent(Player p, String target, ServerManagementEvent event) { super(TYPE); @@ -45,12 +48,14 @@ public class ServerManagementIntent extends Intent { this.event = event; } - public ServerManagementIntent(long time, ServerManagementEvent event) { + public ServerManagementIntent(long time, TimeUnit timeUnit, ServerManagementEvent event) { this(null, null, event); this.time = time; + this.timeUnit = timeUnit; } public long getTime() { return time; } + public TimeUnit getTimeUnit() { return timeUnit; } public Player getPlayer() { return player; } public String getTarget() { return target; } public ServerManagementEvent getEvent() { return event; } diff --git a/src/intents/ServerStatusIntent.java b/src/intents/ServerStatusIntent.java index f9f1ba45..915b5e75 100644 --- a/src/intents/ServerStatusIntent.java +++ b/src/intents/ServerStatusIntent.java @@ -27,6 +27,8 @@ ***********************************************************************************/ package intents; +import java.util.concurrent.TimeUnit; + import resources.control.Intent; import resources.control.ServerStatus; @@ -36,6 +38,7 @@ public class ServerStatusIntent extends Intent { private ServerStatus status; private long time; + private TimeUnit timeUnit; public ServerStatusIntent() { super(TYPE); @@ -47,9 +50,10 @@ public class ServerStatusIntent extends Intent { setStatus(status); } - public ServerStatusIntent(ServerStatus status, long time) { + public ServerStatusIntent(ServerStatus status, long time, TimeUnit timeUnit) { this(status); setTime(time); + this.timeUnit = timeUnit; } public void setTime(long time) { @@ -67,5 +71,9 @@ public class ServerStatusIntent extends Intent { public ServerStatus getStatus() { return status; } + + public TimeUnit getTimeUnit() { + return timeUnit; + } } diff --git a/src/resources/commands/callbacks/ServerCmdCallback.java b/src/resources/commands/callbacks/ServerCmdCallback.java index 437c0bd4..23842dbd 100644 --- a/src/resources/commands/callbacks/ServerCmdCallback.java +++ b/src/resources/commands/callbacks/ServerCmdCallback.java @@ -31,6 +31,8 @@ import intents.ServerManagementIntent; import intents.ServerManagementIntent.ServerManagementEvent; import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import resources.commands.ICmdCallback; import resources.objects.SWGObject; @@ -115,7 +117,7 @@ public class ServerCmdCallback implements ICmdCallback { SuiMessageBox window = new SuiMessageBox(actor, MessageBoxType.YES_NO, "Shutdown Server", "Are you sure you wish to begin the shutdown sequence?"); window.addOkButtonCallback(0, new ISuiCallback() { public void handleEvent(Player player, SWGObject actor, int eventType, List returnParams) { - new ServerManagementIntent(15, ServerManagementEvent.SHUTDOWN).broadcast(); + new ServerManagementIntent(15, TimeUnit.MINUTES, ServerManagementEvent.SHUTDOWN).broadcast(); } }); @@ -123,19 +125,38 @@ public class ServerCmdCallback implements ICmdCallback { } private void handleCustomShutdownServer(Player actor) { - SuiInputBox window = new SuiInputBox(actor, InputBoxType.OK_CANCEL, "Shutdown Server", "Enter the time until the server shuts down. The shutdown sequence " + final SuiListBox unitWindow = new SuiListBox(actor, ListBoxType.OK_CANCEL, "Shutdown Server", "Select the time unit that the time will be specified in. "); + final SuiInputBox timeWindow = new SuiInputBox(actor, InputBoxType.OK_CANCEL, "Shutdown Server", "Enter the time until the server shuts down. The shutdown sequence " + "will begin upon hitting OK."); - window.allowStringCharacters(false); - - window.addInputTextCallback(0, new ISuiCallback() { + final AtomicReference timeUnitReference = new AtomicReference<>(); + final TimeUnit[] unitValues = TimeUnit.values(); + + timeWindow.allowStringCharacters(false); + + // Ziggy: Add all the TimeUnit values as options + for(byte i = 0; i < unitValues.length; i++) + unitWindow.addListItem(unitValues[i].toString(), i); + + unitWindow.addItemSelectionCallback(0, new ISuiCallback() { public void handleEvent(Player player, SWGObject actor, int eventType, List returnParams) { - long countdown = Long.parseLong(returnParams.get(0)); - new ServerManagementIntent(countdown, ServerManagementEvent.SHUTDOWN).broadcast(); + try { + timeUnitReference.set(unitValues[SuiListBox.getSelectedIndex(returnParams)]); + timeWindow.display(); // Ziggy: Display the next window + } catch (ArrayIndexOutOfBoundsException e) { + // Ziggy: This wasn't a valid selection - do nothing + // Should only be possible if they're using some sort of hack to alter the SUI window + } } - }); - window.display(); + timeWindow.addInputTextCallback(0, new ISuiCallback() { + public void handleEvent(Player player, SWGObject actor, int eventType, List returnParams) { + long countdown = Long.parseLong(returnParams.get(0)); + new ServerManagementIntent(countdown, timeUnitReference.get(), ServerManagementEvent.SHUTDOWN).broadcast(); + } + }); + + unitWindow.display(); } } } diff --git a/src/services/CoreManager.java b/src/services/CoreManager.java index 1822409d..ad3ba512 100644 --- a/src/services/CoreManager.java +++ b/src/services/CoreManager.java @@ -129,24 +129,28 @@ public class CoreManager extends Manager { private void handleServerManagementIntent(ServerManagementIntent i) { switch(i.getEvent()) { - case SHUTDOWN: initiateShutdownSequence(i.getTime()); break; + case SHUTDOWN: initiateShutdownSequence(i); break; default: break; } } - private void initiateShutdownSequence(long time) { + private void initiateShutdownSequence(ServerManagementIntent i) { System.out.println("Beginning server shutdown sequence..."); + long time = i.getTime(); + TimeUnit timeUnit = i.getTimeUnit(); - if (time > 0) { - Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() { - public void run() { - shutdownRequested = true; - } - }, time, TimeUnit.MINUTES); - } else { - shutdownRequested = true; - } - new ServerStatusIntent(ServerStatus.SHUTDOWN_REQUESTED, time).broadcast(); + Executors.newSingleThreadScheduledExecutor().schedule( + new Runnable() { + @Override + public void run() { + shutdownRequested = true; + } + // Ziggy: Give the broadcast method extra time to complete. + // If we don't, the final broadcast won't be displayed. + }, + TimeUnit.NANOSECONDS.convert(time, timeUnit) + TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS), TimeUnit.NANOSECONDS); + + new ServerStatusIntent(ServerStatus.SHUTDOWN_REQUESTED, time, i.getTimeUnit()).broadcast(); } public GalaxyStatus getGalaxyStatus() { diff --git a/src/services/chat/ChatService.java b/src/services/chat/ChatService.java index 868dd9db..0292ad13 100644 --- a/src/services/chat/ChatService.java +++ b/src/services/chat/ChatService.java @@ -32,8 +32,10 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import intents.GalacticPacketIntent; import intents.NotifyPlayersPacketIntent; @@ -124,7 +126,7 @@ public class ChatService extends Service { case ServerStatusIntent.TYPE: if (i instanceof ServerStatusIntent) if (((ServerStatusIntent)i).getStatus() == ServerStatus.SHUTDOWN_REQUESTED) - sendShutdownBroadcasts(((ServerStatusIntent) i).getTime()); + sendShutdownBroadcasts((ServerStatusIntent) i); break; } } @@ -309,29 +311,45 @@ public class ChatService extends Service { player.sendPacket(new ChatSystemMessage(SystemChatType.SCREEN_AND_CHAT, pckg)); } - private void sendShutdownBroadcasts(final long time) { + private void sendShutdownBroadcasts(ServerStatusIntent i) { + final TimeUnit timeUnit = i.getTimeUnit(); + final long time = i.getTime(); + final TimeUnit broadcastUnit = TimeUnit.NANOSECONDS; + final long timeNanoSeconds = broadcastUnit.convert(time, timeUnit); + final AtomicLong intervalCount = new AtomicLong((long) Math.ceil(Math.sqrt(Math.sqrt(timeNanoSeconds)) / 200)); + final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); final AtomicInteger runCount = new AtomicInteger(); - final long interval = (int) (time / 3); + final AtomicLong timeRemaining = new AtomicLong(time); + final long timeBetweenBroadcasts; + final String finalShutdownMessage = "The server will now be shutting down."; - Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable() { - public void run() { - switch(runCount.getAndIncrement()) { - case 0: - broadcastGalaxyMessage(String.format("The server will be shutting down in %d minutes.", time), null); - break; - case 1: - broadcastGalaxyMessage(String.format("The server will be shutting down in %d minutes.", time - interval), null); - break; - case 2: - broadcastGalaxyMessage(String.format("The server will be shutting down in %d minutes.", time - (interval*2)), null); - break; - case 3: - broadcastGalaxyMessage("The server will now be shutting down.", null); - throw new RuntimeException("Reached max runCount"); // no "clean" ways to cancel the runnable I can think of + if(intervalCount.get() > timeNanoSeconds) + intervalCount.set(time); + + if(time > 0) { + timeBetweenBroadcasts = timeNanoSeconds / intervalCount.get(); + + executor.scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + String message; + long timeRemainingVal = timeRemaining.get(); + + if(runCount.getAndIncrement() == intervalCount.get()) { + message = finalShutdownMessage; + executor.shutdown(); + } else { + String unitName = timeUnit.name().toLowerCase(Locale.ENGLISH); + message = String.format("The server will be shutting down in %d %s", timeRemainingVal, unitName.substring(0, unitName.length() - 1)); + message += (timeRemainingVal == 1 ? "" : "s") + "."; + timeRemaining.set(timeRemainingVal - timeUnit.convert(timeBetweenBroadcasts, broadcastUnit)); + } + + broadcastGalaxyMessage(message, null); } - } - }, 0, interval, TimeUnit.MINUTES); - + }, 0, timeBetweenBroadcasts, broadcastUnit); + } else + broadcastGalaxyMessage(finalShutdownMessage, null); } private void sendPersistentMessageHeaders(Player player, String galaxy) {