commit cc8e3f5b79f4be7267794eef0caf251513ca2264 Author: BuildTools Date: Sat Jun 19 19:46:54 2021 +0200 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..46270ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Project exclude paths +/target/ +/.idea/ \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..41fa513 --- /dev/null +++ b/pom.xml @@ -0,0 +1,82 @@ + + + 4.0.0 + + + 11 + 11 + UTF-8 + + + nl.iobyte + themeparkconnector + 3.0.0 + + + + vault-repo + http://nexus.hc.to/content/repositories/pub_releases + + + codemc-repo + https://repo.codemc.org/repository/maven-public/ + default + + + + + + org.spigotmc + spigot-api + 1.12.2-R0.1-SNAPSHOT + provided + + + io.socket + socket.io-client + 1.0.1 + + + nl.iobyte + themepark + 3.0.0 + provided + + + de.mkammerer.snowflake-id + snowflake-id + 0.0.1 + + + de.tr7zw + item-nbt-api + 2.8.0 + + + net.milkbowl.vault + VaultAPI + 1.7 + provided + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.0 + + + package + + shade + + + + + + + \ No newline at end of file diff --git a/src/main/java/nl/iobyte/themeparkconnector/ThemeParkConnector.java b/src/main/java/nl/iobyte/themeparkconnector/ThemeParkConnector.java new file mode 100644 index 0000000..eee554c --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/ThemeParkConnector.java @@ -0,0 +1,103 @@ +package nl.iobyte.themeparkconnector; + +import com.google.gson.Gson; +import net.milkbowl.vault.economy.Economy; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themeparkconnector.api.config.enums.StorageKey; +import nl.iobyte.themeparkconnector.commands.ThemeParkConnectorCommand; +import nl.iobyte.themeparkconnector.listeners.*; +import nl.iobyte.themeparkconnector.sbd.License; +import nl.iobyte.themeparkconnector.sbd.UpdateManager; +import org.bukkit.Bukkit; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.RegisteredServiceProvider; +import org.bukkit.plugin.java.JavaPlugin; + +public class ThemeParkConnector extends JavaPlugin { + + private final static Gson gson = ThemeParkConnectorGson.getInstance(); + + private static ThemeParkConnector instance; + private final ThemeParkConnectorAPI api = new ThemeParkConnectorAPI(); + private Economy econ = null; + private boolean disabling = false; + + public void onEnable() { + if (!setupEconomy() ) { + Bukkit.getLogger().info(String.format("[%s] - No Vault dependency found!", getDescription().getName())); + } + + instance = this; + api.enable(); + + //License stuff + new License(this, "TPP", api.getConfigurationManager().getString(StorageKey.LICENSE)); + + loadCommands(); + loadListeners(); + + //Check Version + new UpdateManager(this, 4, UpdateManager.CheckType.SBDPLUGINS).handleResponse((versionResponse, version) -> { + if (versionResponse == UpdateManager.VersionResponse.FOUND_NEW) { + Bukkit.getLogger().warning("[ThemeParkConnector] There is a new version available! Curent: " + getDescription().getVersion() + " New: " + version); + } else if (versionResponse == UpdateManager.VersionResponse.LATEST) { + Bukkit.getLogger().info("[ThemeParkConnector] You are running the latest version [" + getDescription().getVersion() + "]!"); + } else if (versionResponse == UpdateManager.VersionResponse.UNAVAILABLE) { + Bukkit.getLogger().severe("[ThemeParkConnector] Unable to perform an update check."); + } + }).check(); + } + + private boolean setupEconomy() { + if(getServer().getPluginManager().getPlugin("Vault") == null) + return false; + + RegisteredServiceProvider rsp = getServer().getServicesManager().getRegistration(Economy.class); + if (rsp == null) + return false; + + econ = rsp.getProvider(); + return econ != null; + } + + private void loadCommands() { + new ThemeParkConnectorCommand(); + } + + private void loadListeners() { + PluginManager pm = Bukkit.getPluginManager(); + pm.registerEvents(new PlayerListener(), this); + pm.registerEvents(new OperatorListener(), this); + pm.registerEvents(new AttractionListener(), this); + pm.registerEvents(new RideCountListener(), this); + pm.registerEvents(new TicketListener(), this); + pm.registerEvents(new RideOperatorListener(), this); + } + + public void onDisable() { + disabling = true; + api.disable(); + + instance = null; + } + + public static Gson getGson() { + return gson; + } + + public static ThemeParkConnector getInstance() { + return instance; + } + + public Economy getEconomy() { + return econ; + } + + public ThemeParkConnectorAPI getAPI() { + return api; + } + + public boolean isDisabling() { + return disabling; + } +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/ThemeParkConnectorGson.java b/src/main/java/nl/iobyte/themeparkconnector/ThemeParkConnectorGson.java new file mode 100644 index 0000000..25357ca --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/ThemeParkConnectorGson.java @@ -0,0 +1,30 @@ +package nl.iobyte.themeparkconnector; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import nl.iobyte.themepark.api.attraction.enums.Status; +import nl.iobyte.themeparkconnector.api.operator.adapter.OperatorItemAdapter; +import nl.iobyte.themeparkconnector.api.operator.adapter.OperatorPanelAdapter; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItem; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorPanel; +import nl.iobyte.themeparkconnector.api.packet.adapter.PacketPayloadAdapter; +import nl.iobyte.themeparkconnector.api.packet.adapter.StatusAdapter; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; + +public class ThemeParkConnectorGson { + + public static Gson getInstance() { + GsonBuilder builder = new GsonBuilder(); + + //Packet + builder.registerTypeAdapter(AbstractPacketPayload.class, new PacketPayloadAdapter()) + .registerTypeAdapter(Status.class, new StatusAdapter()); + + //Operator + builder.registerTypeAdapter(OperatorPanel.class, new OperatorPanelAdapter()) + .registerTypeAdapter(OperatorItem.class, new OperatorItemAdapter()); + + return builder.create(); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/ThemeParkConnectorAPI.java b/src/main/java/nl/iobyte/themeparkconnector/api/ThemeParkConnectorAPI.java new file mode 100644 index 0000000..a64f51a --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/ThemeParkConnectorAPI.java @@ -0,0 +1,133 @@ +package nl.iobyte.themeparkconnector.api; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.authentication.AuthenticationService; +import nl.iobyte.themeparkconnector.api.config.ConfigurationManager; +import nl.iobyte.themeparkconnector.api.event.EventDispatcher; +import nl.iobyte.themeparkconnector.api.load.DataLoadService; +import nl.iobyte.themeparkconnector.api.network.NetworkingService; +import nl.iobyte.themeparkconnector.api.operator.OperatorService; +import nl.iobyte.themeparkconnector.api.player.PlayerStateService; +import nl.iobyte.themeparkconnector.api.show.ShowService; +import nl.iobyte.themeparkconnector.api.state.StateService; +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; + +public class ThemeParkConnectorAPI { + + //Data + private ConfigurationManager configurationManager; + private final DataLoadService dataLoadService = new DataLoadService(); + private final ShowService showService = new ShowService(); + + //ThemeParkConnector System + private final NetworkingService networkingService = new NetworkingService(); + private final StateService stateService = new StateService(); + private final PlayerStateService playerStateService = new PlayerStateService(); + private AuthenticationService authenticationService; + + //Attraction + private final OperatorService operatorService = new OperatorService(); + + //General + private EventDispatcher eventDispatcher; + + /** + * Enable the API + */ + public void enable() { + eventDispatcher = new EventDispatcher(ThemeParkConnector.getInstance()); + + //Prepare Configuration + configurationManager = new ConfigurationManager(ThemeParkConnector.getInstance()); + + //Load data + dataLoadService.init(); + authenticationService = new AuthenticationService(); + networkingService.init(); + } + + /** + * Disable the API + */ + public void disable() { + try { + if (stateService.isConnected()) + networkingService.stop(); + } catch (NoClassDefFoundError e) { + ThemeParkConnectorLogger.toConsole("Bukkit already unloaded the networking classes, can't kill socket."); + } + } + + /** + * Get the show service + * @return ShowService + */ + public ShowService getShowService() { + return showService; + } + + /** + * Get the networking service + * @return NetworkingService + */ + public NetworkingService getNetworkingService() { + return networkingService; + } + + /** + * Get the configuration manager + * @return ConfigurationManager + */ + public ConfigurationManager getConfigurationManager() { + return configurationManager; + } + + /** + * Get the state service + * @return StateService + */ + public StateService getStateService() { + return stateService; + } + + /** + * Get the player state service + * @return PlayerStateService + */ + public PlayerStateService getPlayerStateService() { + return playerStateService; + } + + /** + * Get the data load service + * @return DataLoadService + */ + public DataLoadService getDataLoadService() { + return dataLoadService; + } + + /** + * Get the operator service + * @return OperatorService + */ + public OperatorService getOperatorService() { + return operatorService; + } + + /** + * Get event dispatcher + * @return EventDispatcher + */ + public EventDispatcher getEventDispatcher() { + return eventDispatcher; + } + + /** + * Get authentication service + * @return AuthenticationService + */ + public AuthenticationService getAuthenticationService() { + return authenticationService; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/authentication/AuthenticationService.java b/src/main/java/nl/iobyte/themeparkconnector/api/authentication/AuthenticationService.java new file mode 100644 index 0000000..fe84089 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/authentication/AuthenticationService.java @@ -0,0 +1,50 @@ +package nl.iobyte.themeparkconnector.api.authentication; + +import de.mkammerer.snowflakeid.SnowflakeIdGenerator; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.authentication.objects.WeakConcurrentHashMap; +import java.util.Map; +import java.util.UUID; + +public class AuthenticationService { + + private final SnowflakeIdGenerator generator = SnowflakeIdGenerator.createDefault(42); + private final Map tokens = new WeakConcurrentHashMap<>(ThemeParkConnector.getInstance(), 15 * 1000); + private final Map attractions = new WeakConcurrentHashMap<>(ThemeParkConnector.getInstance(), 15 * 1000); + + public String generateToken(UUID uuid, String id) { + long snowflake = generator.next(); + + //ID to Token and store Token + String token = Long.toString(snowflake); + tokens.put(uuid, token); + attractions.put(uuid, id); + + return token; + } + + public boolean hasToken(UUID uuid) { + return tokens.containsKey(uuid); + } + + public boolean matchToken(UUID uuid, String token, String id) { + String str = tokens.get(uuid); + if(str == null || str.isEmpty()) + return false; + + if(!str.equals(token)) + return false; + + str = attractions.get(uuid); + if(str == null || str.isEmpty()) + return false; + + if(!str.equals(id)) + return false; + + tokens.remove(uuid); + attractions.remove(uuid); + return true; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/authentication/objects/WeakConcurrentHashMap.java b/src/main/java/nl/iobyte/themeparkconnector/api/authentication/objects/WeakConcurrentHashMap.java new file mode 100644 index 0000000..5376170 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/authentication/objects/WeakConcurrentHashMap.java @@ -0,0 +1,151 @@ +package nl.iobyte.themeparkconnector.api.authentication.objects; + +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; +import java.util.Date; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A Weak Concurrent HashMap Solution which stores the keys and values only for a specific amount of time, and then expires after that + * time. + * + * This HashMap uses the Bukkit scheduler for the cleanup. + * + * @author Vivekananthan M, Stijn Bannink + * + * @param The key in the hashmap + * @param The value in the hashmap + */ +public class WeakConcurrentHashMap extends ConcurrentHashMap { + + private static final long serialVersionUID = 1L; + + private final Map timeMap = new ConcurrentHashMap<>(); + private final long expiryInMillis; + + private Listener listener; + private boolean mapAlive = true; + + public WeakConcurrentHashMap(JavaPlugin plugin) { + this.expiryInMillis = 10000; + initialize(plugin); + } + + public WeakConcurrentHashMap(JavaPlugin plugin, Listener listener) { + this.listener = listener; + this.expiryInMillis = 10000; + initialize(plugin); + } + + public WeakConcurrentHashMap(JavaPlugin plugin, long expiryInMillis) { + this.expiryInMillis = expiryInMillis; + initialize(plugin); + } + + public WeakConcurrentHashMap(JavaPlugin plugin, long expiryInMillis, Listener listener) { + this.expiryInMillis = expiryInMillis; + this.listener = listener; + initialize(plugin); + } + + private void initialize(JavaPlugin plugin) { + new CleanerRunnable().runTaskTimerAsynchronously(plugin, expiryInMillis / 2, expiryInMillis / 2); + } + + /** + * {@inheritDoc} + * + * @throws IllegalStateException if trying to insert values into map after quiting + */ + @Override + public V put(K key, V value) { + if (!mapAlive) { + throw new IllegalStateException("WeakConcurrent Hashmap is no more alive.. Try creating a new one."); // No I18N + } + Date date = new Date(); + timeMap.put(key, date.getTime()); + V returnVal = super.put(key, value); + if (listener != null) { + listener.notifyOnAdd(key, value); + } + return returnVal; + } + + /** + * {@inheritDoc} + * + * @throws IllegalStateException if trying to insert values into map after quiting + */ + @Override + public void putAll(Map m) { + if (!mapAlive) { + throw new IllegalStateException("WeakConcurrent Hashmap is no more alive.. Try creating a new one."); // No I18N + } + for (K key : m.keySet()) { + put(key, m.get(key)); + } + } + + /** + * {@inheritDoc} + * + * @throws IllegalStateException if trying to insert values into map after quiting + */ + @Override + public V putIfAbsent(K key, V value) { + if (!mapAlive) { + throw new IllegalStateException("WeakConcurrent Hashmap is no more alive.. Try creating a new one."); // No I18N + } + if (!containsKey(key)) { + return put(key, value); + } else { + return get(key); + } + } + + /** + * Should call this method when it's no longer required + */ + public void quitMap() { + mapAlive = false; + } + + public boolean isAlive() { + return mapAlive; + } + + /** + * + * This thread performs the cleaning operation on the concurrent hashmap once in a specified interval. This wait interval is half of the + * time from the expiry time. + * + * + */ + private class CleanerRunnable extends BukkitRunnable { + @Override + public void run() { + if (mapAlive) cleanMap(); + else cancel(); + } + + private void cleanMap() { + long currentTime = new Date().getTime(); + for (K key : timeMap.keySet()) { + if (currentTime > (timeMap.get(key) + expiryInMillis)) { + V value = remove(key); + timeMap.remove(key); + if (listener != null) { + listener.notifyOnRemoval(key, value); + } + } + } + } + } + + public interface Listener { + void notifyOnAdd(K key, V value); + void notifyOnRemoval(K key, V value); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/client/ClientConnection.java b/src/main/java/nl/iobyte/themeparkconnector/api/client/ClientConnection.java new file mode 100644 index 0000000..a4d7836 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/client/ClientConnection.java @@ -0,0 +1,182 @@ +package nl.iobyte.themeparkconnector.api.client; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.message.MessageKey; +import nl.iobyte.themeparkconnector.api.network.packet.KickClientPacket; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class ClientConnection { + + //General + private final UUID uuid; + private final String name; + + //Connect + private boolean waitingForToken = false; + private boolean isConnected = false; + private String usedToken = null; + private String attraction; + private final List onConnect = new ArrayList<>(); + private final List onDisconnect = new ArrayList<>(); + private final Publisher publisher; + + public ClientConnection(Player player) { + this.uuid = player.getUniqueId(); + this.name = player.getName(); + this.publisher = new Publisher(this); + } + + /** + * Get Name of Client + * @return Client Name + */ + public String getOwnerName() { + return name; + } + + /** + * Get UUID of Client + * @return Client UUID + */ + public UUID getOwnerUUID() { + return uuid; + } + + /** + * Get Player + * @return Player instance + */ + public Player getPlayer() { + return Bukkit.getPlayer(getOwnerUUID()); + } + + /** + * Check if client is connected + * @return connection boolean + */ + public boolean isConnected() { + return isConnected; + } + + /** + * When client connection is opened + */ + public void onConnect() { + if(isConnected) + return; + + isConnected = true; + MessageKey.CLIENT_CONNECTION_OPEN.send(getPlayer()); + for(Runnable runnable : onConnect) + runnable.run(); + } + + /** + * When client connection is closed + */ + public void onDisconnect() { + if(!isConnected) + return; + + isConnected = false; + usedToken = null; + ThemeParkConnector.getInstance().getAPI().getOperatorService().stopOperating(uuid); + MessageKey.CLIENT_CONNECTION_CLOSED.send(getPlayer()); + for(Runnable runnable : onDisconnect) + runnable.run(); + } + + /** + * Get token used to connect + * @return String + */ + public String getUsedToken() { + return usedToken; + } + + /** + * Set token used to connect + * @param token String + */ + public void setUsedToken(String token) { + usedToken = token; + } + + /** + * Get if client is waiting for token + * @return boolean + */ + public boolean isWaitingForToken() { + return waitingForToken; + } + + /** + * Set if client is waiting for token + * @param waitingForToken boolean + */ + public void setWaitingForToken(boolean waitingForToken) { + this.waitingForToken = waitingForToken; + } + + /** + * Set if client is waiting for token + * @param waitingForToken boolean + */ + public void setWaitingForToken(boolean waitingForToken, String attraction) { + setWaitingForToken(waitingForToken); + this.attraction = attraction; + } + + public void publishURL() { + publisher.startClientSession(attraction); + } + + /** + * Try generating url for client + */ + public void publishURL(String id) { + publisher.startClientSession(id); + } + + /** + * Close the clients web client + */ + public void kick() { + ThemeParkConnector.getInstance().getAPI().getNetworkingService().send(this, new KickClientPacket()); + ThemeParkConnector.getInstance().getAPI().getOperatorService().stopOperating(uuid); + } + + /** + * When ClientConnection gets removed + */ + public void remove() { + ThemeParkConnector.getInstance().getAPI().getNetworkingService().send(this, new KickClientPacket()); + ThemeParkConnector.getInstance().getAPI().getOperatorService().stopOperating(uuid); + } + + /** + * Add handler run on connect + * @param runnable Runnable + * @return ClientConnection + */ + public ClientConnection addOnConnectHandler(Runnable runnable) { + onConnect.add(runnable); + return this; + } + + /** + * Add handler run on disconnect + * @param runnable Runnable + * @return ClientConnection + */ + public ClientConnection addOnDisconnectHandler(Runnable runnable) { + onDisconnect.add(runnable); + return this; + } + + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/client/Publisher.java b/src/main/java/nl/iobyte/themeparkconnector/api/client/Publisher.java new file mode 100644 index 0000000..65e79d1 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/client/Publisher.java @@ -0,0 +1,72 @@ +package nl.iobyte.themeparkconnector.api.client; + +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import nl.iobyte.themepark.scheduler.ThemeParkScheduler; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.config.enums.StorageKey; +import nl.iobyte.themeparkconnector.api.message.MessageKey; +import nl.iobyte.themeparkconnector.api.state.objects.AbstractState; +import nl.iobyte.themeparkconnector.api.state.states.IdleState; + +public class Publisher { + + private final ClientConnection clientConnection; + + public Publisher(ClientConnection clientConnection) { + this.clientConnection = clientConnection; + } + + /** + * Attempt to generate url + */ + public void startClientSession(String id) { + if(id == null) + return; + + //Check if already connected + if (clientConnection.isConnected()) { + MessageKey.CLIENT_CONNECTION_EXISTS.send(clientConnection.getPlayer()); + return; + } + + if(!clientConnection.isWaitingForToken()) + MessageKey.CLIENT_GENERATE_TO_CONNECT.send(clientConnection.getPlayer()); + + //Check if state allows connections + AbstractState currentState = ThemeParkConnector.getInstance().getAPI().getStateService().getCurrentState(); + if(!currentState.canClientConnect()) { + clientConnection.setWaitingForToken(true, id); + if(currentState instanceof IdleState) + ThemeParkScheduler.runAsync(() -> ThemeParkConnector.getInstance().getAPI().getNetworkingService().connectIfDown()); + + return; + } + + //Sending waiting message + clientConnection.setWaitingForToken(true, id); + String token = ThemeParkConnector.getInstance().getAPI().getAuthenticationService().generateToken(clientConnection.getOwnerUUID(), id); + + String url = ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SOCKET_PANEL_URL); + url = url.replaceAll("%TOKEN%", token); + url = url.replaceAll("%ID%", id); + + TextComponent message = new TextComponent( + MessageKey.CLIENT_CLICK_TO_CONNECT.getMessage() + ); + + TextComponent[] hover = new TextComponent[]{ + new TextComponent( + MessageKey.CLIENT_HOVER_TO_CONNECT.getMessage() + ) + }; + + message.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)); + message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, hover)); + + clientConnection.getPlayer().spigot().sendMessage(message); + clientConnection.setWaitingForToken(false, null); + } + +} \ No newline at end of file diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/client/objects/Notification.java b/src/main/java/nl/iobyte/themeparkconnector/api/client/objects/Notification.java new file mode 100644 index 0000000..c27f05d --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/client/objects/Notification.java @@ -0,0 +1,56 @@ +package nl.iobyte.themeparkconnector.api.client.objects; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.network.packet.NotificationPacket; + +public class Notification { + + private String title, message; + + public Notification(String title, String message) { + this.title = title; + this.message = message; + } + + /** + * Get Title of Notification + * @return String + */ + public String getTitle() { + return title; + } + + /** + * Set Title of Notification + * @param title String + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * Get Message of Notification + * @return String + */ + public String getMessage() { + return message; + } + + /** + * Set Message of Notification + * @param message String + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * Send Notification to client + * @param client ClientConnection + */ + public void send(ClientConnection client) { + ThemeParkConnector.getInstance().getAPI().getNetworkingService().send(client, new NotificationPacket(this)); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/config/ConfigurationManager.java b/src/main/java/nl/iobyte/themeparkconnector/api/config/ConfigurationManager.java new file mode 100644 index 0000000..62f2b2e --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/config/ConfigurationManager.java @@ -0,0 +1,252 @@ +package nl.iobyte.themeparkconnector.api.config; + +import nl.iobyte.themepark.api.config.objects.Configuration; +import nl.iobyte.themeparkconnector.api.config.enums.StorageKey; +import nl.iobyte.themeparkconnector.api.config.enums.StorageLocation; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.plugin.Plugin; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ConfigurationManager { + + private final Map locations = new HashMap<>(); + + public ConfigurationManager(Plugin plugin) { + for(StorageLocation location : StorageLocation.values()) + locations.put(location, new Configuration(plugin, location.getName(), true)); + } + + /** + * Get configuration instance belonging to a storage location + * @param location StorageLocation + * @return Configuration instance + */ + public Configuration getConfiguration(StorageLocation location) { + if(location == null) + return null; + + return locations.get(location); + } + + /** + * Save specific storage location + * @param location StorageLocation + */ + public void save(StorageLocation location) { + if(location == null) + return; + + getConfiguration(location).save(); + } + + /** + * Save all storage locations + */ + public void saveAll() { + for(Configuration configuration : locations.values()) + configuration.save(); + } + + /** + * Check if configuration contains storage key + * @param key StorageKey + * @return Boolean + */ + public boolean contains(StorageKey key) { + if(key == null) + return false; + + return contains(key.getLocation(), key.getPath()); + } + + /** + * Check if storage location contains path + * @param location StorageLocation + * @param path String + * @return Boolean + */ + public boolean contains(StorageLocation location, String path) { + if(location == null) + return false; + + return getConfiguration(location).contains(path); + } + + /** + * Set value to StorageKey + * @param key StorageKey + * @param value Object to set StorageKey to + */ + public void set(StorageKey key, Object value) { + if(key == null) + return; + + set(key.getLocation(), key.getPath(), value); + } + + /** + * Set value to path in storage location + * @param location StorageLocation + * @param path String + * @param value Object to set path to + */ + public void set(StorageLocation location, String path, Object value) { + if(location == null) + return; + + getConfiguration(location).set(path, value); + } + + /** + * Get object value of storage key + * @param key StorageKey + * @return Object + */ + public Object get(StorageKey key) { + if(key == null) + return null; + + return get(key.getLocation(), key.getPath()); + } + + /** + * Get object value of path in storage location + * @param location StorageLocation + * @param path Path + * @return Object + */ + public Object get(StorageLocation location, String path) { + if(location == null) + return null; + + return getConfiguration(location).get(path); + } + + /** + * Get string value of storage key + * @param key StorageKey + * @return String + */ + public String getString(StorageKey key) { + if(key == null) + return null; + + return getString(key.getLocation(), key.getPath()); + } + + /** + * Get string value of path in storage location + * @param location StorageLocation + * @param path Path + * @return String + */ + public String getString(StorageLocation location, String path) { + if(location == null) + return null; + + return getConfiguration(location).getString(path); + } + + /** + * Get int value of storage key + * @param key StorageKey + * @return int + */ + public int getInt(StorageKey key) { + if(key == null) + return 0; + + return getInt(key.getLocation(), key.getPath()); + } + + /** + * Get int value of path in storage location + * @param location StorageLocation + * @param path Path + * @return int + */ + public int getInt(StorageLocation location, String path) { + if(location == null) + return 0; + + return getConfiguration(location).getInt(path); + } + + /** + * Get boolean value of storage key + * @param key StorageKey + * @return boolean + */ + public boolean getBoolean(StorageKey key) { + if(key == null) + return false; + + return getBoolean(key.getLocation(), key.getPath()); + } + + /** + * Get boolean value of path in storage location + * @param location StorageLocation + * @param path Path + * @return boolean + */ + public boolean getBoolean(StorageLocation location, String path) { + if(location == null) + return false; + + return getConfiguration(location).getBoolean(path); + } + + /** + * Get string list value of storage key + * @param key StorageKey + * @return List + */ + public List getStringList(StorageKey key) { + if(key == null) + return null; + + return getStringList(key.getLocation(), key.getPath()); + } + + /** + * Get string list value of path in storage location + * @param location StorageLocation + * @param path Path + * @return List + */ + public List getStringList(StorageLocation location, String path) { + if(location == null) + return null; + + return getConfiguration(location).getStringList(path); + } + + /** + * Get string list value of storage key + * @param key StorageKey + * @return List + */ + public ConfigurationSection getSection(StorageKey key) { + if(key == null) + return null; + + return getSection(key.getLocation(), key.getPath()); + } + + /** + * Get string list value of path in storage location + * @param location StorageLocation + * @param path Path + * @return List + */ + public ConfigurationSection getSection(StorageLocation location, String path) { + if(location == null) + return null; + + return getConfiguration(location).getSection(path); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/config/enums/StorageKey.java b/src/main/java/nl/iobyte/themeparkconnector/api/config/enums/StorageKey.java new file mode 100644 index 0000000..f7b0c52 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/config/enums/StorageKey.java @@ -0,0 +1,43 @@ +package nl.iobyte.themeparkconnector.api.config.enums; + +public enum StorageKey { + + //General + LICENSE(StorageLocation.SETTINGS, "license"), + ACTIONFOTO(StorageLocation.SETTINGS, "actionfoto"), + + //Sign + SIGN_CONTROL_NAME(StorageLocation.SETTINGS, "sign.control.name"), + SIGN_CONTROL_TITLE(StorageLocation.SETTINGS, "sign.control.title"), + SIGN_SCAN_NAME(StorageLocation.SETTINGS, "sign.scan.name"), + SIGN_SCAN_TITLE(StorageLocation.SETTINGS, "sign.scan.title"), + + //Socket + SOCKET_ID(StorageLocation.SETTINGS, "socket.id"), + SOCKET_URL(StorageLocation.SETTINGS, "socket.url"), + SOCKET_PANEL_URL(StorageLocation.SETTINGS, "socket.panel"); + + private final StorageLocation location; + private final String path; + StorageKey(StorageLocation location, String path) { + this.location = location; + this.path = path; + } + + /** + * Get storage location key is stored in + * @return StorageLocation + */ + public StorageLocation getLocation() { + return location; + } + + /** + * Get path to value + * @return String + */ + public String getPath() { + return path; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/config/enums/StorageLocation.java b/src/main/java/nl/iobyte/themeparkconnector/api/config/enums/StorageLocation.java new file mode 100644 index 0000000..f16aece --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/config/enums/StorageLocation.java @@ -0,0 +1,21 @@ +package nl.iobyte.themeparkconnector.api.config.enums; + +public enum StorageLocation { + + SETTINGS("settings.yml"), + MESSAGE("message.yml"); + + private final String name; + StorageLocation(String name) { + this.name = name; + } + + /** + * Returns filename of StorageLocation + * @return String + */ + public String getName() { + return name; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/event/EventDispatcher.java b/src/main/java/nl/iobyte/themeparkconnector/api/event/EventDispatcher.java new file mode 100644 index 0000000..21e3f39 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/event/EventDispatcher.java @@ -0,0 +1,45 @@ +package nl.iobyte.themeparkconnector.api.event; + +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; +import org.bukkit.Bukkit; +import org.bukkit.event.Event; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; + +public class EventDispatcher { + + private final Plugin plugin; + + public EventDispatcher(Plugin plugin) { + this.plugin = plugin; + } + + /** + * Dispatch Event without Callback + * @param event Event + */ + public void call(Event event) { + call(event, null); + } + + /** + * Dispatch Event with Callback + * @param event Event + * @param callback Runnable + */ + public void call(Event event, Runnable callback) { + PluginManager pm = Bukkit.getPluginManager(); + if(Bukkit.isPrimaryThread()) { + pm.callEvent(event); + if(callback != null) + callback.run(); + } else { + Bukkit.getScheduler().runTask(plugin, () -> { + pm.callEvent(event); + if(callback != null) + Bukkit.getScheduler().runTaskAsynchronously(plugin, callback); + }); + } + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/event/objects/ChangeEvent.java b/src/main/java/nl/iobyte/themeparkconnector/api/event/objects/ChangeEvent.java new file mode 100644 index 0000000..62a8257 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/event/objects/ChangeEvent.java @@ -0,0 +1,32 @@ +package nl.iobyte.themeparkconnector.api.event.objects; + +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; + +public class ChangeEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + private final T old, current; + + public ChangeEvent(T old, T current) { + this.old = old; + this.current = current; + } + + public T getOld() { + return old; + } + + public T getCurrent() { + return current; + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/event/objects/OperatorEvent.java b/src/main/java/nl/iobyte/themeparkconnector/api/event/objects/OperatorEvent.java new file mode 100644 index 0000000..b6e0c0a --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/event/objects/OperatorEvent.java @@ -0,0 +1,18 @@ +package nl.iobyte.themeparkconnector.api.event.objects; + +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; + +public class OperatorEvent extends ChangeEvent { + + private final AttractionOperator operator; + + public OperatorEvent(AttractionOperator operator, T old, T current) { + super(old, current); + this.operator = operator; + } + + public AttractionOperator getOperator() { + return operator; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorConnectEvent.java b/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorConnectEvent.java new file mode 100644 index 0000000..9a0bbe8 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorConnectEvent.java @@ -0,0 +1,14 @@ +package nl.iobyte.themeparkconnector.api.event.operator; + +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.event.objects.OperatorEvent; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; + +public class OperatorConnectEvent extends OperatorEvent { + + public OperatorConnectEvent(AttractionOperator operator, ClientConnection connection) { + super(operator, null, connection); + } + +} + diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorDisconnectEvent.java b/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorDisconnectEvent.java new file mode 100644 index 0000000..4fefea6 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorDisconnectEvent.java @@ -0,0 +1,14 @@ +package nl.iobyte.themeparkconnector.api.event.operator; + +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.event.objects.OperatorEvent; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; + +public class OperatorDisconnectEvent extends OperatorEvent { + + public OperatorDisconnectEvent(AttractionOperator operator, ClientConnection connection) { + super(operator, connection, null); + } + +} + diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorStateEvent.java b/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorStateEvent.java new file mode 100644 index 0000000..83587b8 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/event/operator/OperatorStateEvent.java @@ -0,0 +1,28 @@ +package nl.iobyte.themeparkconnector.api.event.operator; + +import nl.iobyte.themeparkconnector.api.event.objects.OperatorEvent; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItem; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItemState; + +public class OperatorStateEvent extends OperatorEvent { + + private final OperatorItem item; + private final String data; + + public OperatorStateEvent(AttractionOperator operator, OperatorItem item, OperatorItemState old, OperatorItemState current, String data) { + super(operator, old, current); + this.item = item; + this.data = data; + } + + public OperatorItem getItem() { + return item; + } + + public String getData() { + return data; + } + +} + diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/event/ticket/TicketScanEvent.java b/src/main/java/nl/iobyte/themeparkconnector/api/event/ticket/TicketScanEvent.java new file mode 100644 index 0000000..cf0e90c --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/event/ticket/TicketScanEvent.java @@ -0,0 +1,55 @@ +package nl.iobyte.themeparkconnector.api.event.ticket; + +import nl.iobyte.themeparkconnector.api.show.objects.Ticket; +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import java.util.UUID; + +public class TicketScanEvent extends Event implements Cancellable { + + private static final HandlerList handlers = new HandlerList(); + + private final UUID uuid; + private final Ticket ticket; + private final boolean valid; + + private boolean cancel = false; + + public TicketScanEvent(UUID uuid, Ticket ticket, boolean valid) { + this.uuid = uuid; + this.ticket = ticket; + this.valid = valid; + } + + public UUID getUUID() { + return uuid; + } + + public Ticket getTicket() { + return ticket; + } + + public boolean isValid() { + return valid; + } + + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return cancel; + } + + @Override + public void setCancelled(boolean b) { + cancel = b; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/load/DataLoadService.java b/src/main/java/nl/iobyte/themeparkconnector/api/load/DataLoadService.java new file mode 100644 index 0000000..f531aa0 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/load/DataLoadService.java @@ -0,0 +1,33 @@ +package nl.iobyte.themeparkconnector.api.load; + +import nl.iobyte.themeparkconnector.api.load.interfaces.IDataLoader; +import nl.iobyte.themeparkconnector.api.load.objects.*; +import java.util.ArrayList; +import java.util.List; + +public class DataLoadService { + + private final List loaders = new ArrayList<>(); + + public DataLoadService() { + addLoader(new DatabaseLoader()); + addLoader(new OperatorDataLoader()); + } + + /** + * Add IDataLoader + * @param dataLoader IDataLoader + */ + public void addLoader(IDataLoader dataLoader) { + loaders.add(dataLoader); + } + + /** + * Call all IDataLoaders + */ + public void init() { + for(IDataLoader loader : loaders) + loader.load(); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/load/interfaces/IDataLoader.java b/src/main/java/nl/iobyte/themeparkconnector/api/load/interfaces/IDataLoader.java new file mode 100644 index 0000000..73f8ca5 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/load/interfaces/IDataLoader.java @@ -0,0 +1,10 @@ +package nl.iobyte.themeparkconnector.api.load.interfaces; + +public interface IDataLoader { + + /** + * Called when loading data + */ + void load(); + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/load/objects/DatabaseLoader.java b/src/main/java/nl/iobyte/themeparkconnector/api/load/objects/DatabaseLoader.java new file mode 100644 index 0000000..0b34d04 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/load/objects/DatabaseLoader.java @@ -0,0 +1,36 @@ +package nl.iobyte.themeparkconnector.api.load.objects; + +import nl.iobyte.themepark.ThemePark; +import nl.iobyte.themepark.api.database.DatabaseService; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.config.enums.StorageKey; +import nl.iobyte.themeparkconnector.api.load.interfaces.IDataLoader; + +public class DatabaseLoader implements IDataLoader { + + public void load() { + DatabaseService service = ThemePark.getInstance().getAPI().getDatabaseService(); + + //Create MySQL Tables + service.execute( + "remote", + "CREATE TABLE IF NOT EXISTS ridecounts(uuid VARCHAR(255) NOT NULL, attraction_id VARCHAR(255) NOT NULL, year SMALLINT(2), month SMALLINT(2), week SMALLINT(2), day SMALLINT(2), count SMALLINT(2), UNIQUE(uuid, attraction_id, year, day))", + null + ); + + service.execute( + "remote", + "CREATE TABLE IF NOT EXISTS seats(id BIGINT(20) NOT NULL AUTO_INCREMENT, show_id VARCHAR(256) NOT NULL, vault_price VARCHAR(256) NOT NULL, date TIMESTAMP NOT NULL, seat INT(11) NOT NULL, uuid VARCHAR(256) NOT NULL, loaded TINYINT(1) DEFAULT 0, paid TINYINT(1) DEFAULT 0, UNIQUE(show_id, date, uuid), UNIQUE(show_id, seat, date), PRIMARY KEY(id))", + null + ); + + if(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getBoolean(StorageKey.ACTIONFOTO)) { + service.execute( + "remote", + "CREATE TABLE IF NOT EXISTS actionfotos (id BIGINT(20) NOT NULL AUTO_INCREMENT, uuid VARCHAR(256) NOT NULL, ride VARCHAR(256) NOT NULL, base64 BLOB NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(), UNIQUE(uuid, ride), PRIMARY KEY(id))", + null + ); + } + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/load/objects/OperatorDataLoader.java b/src/main/java/nl/iobyte/themeparkconnector/api/load/objects/OperatorDataLoader.java new file mode 100644 index 0000000..e5d00cf --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/load/objects/OperatorDataLoader.java @@ -0,0 +1,250 @@ +package nl.iobyte.themeparkconnector.api.load.objects; + +import nl.iobyte.themepark.ThemePark; +import nl.iobyte.themepark.api.ThemeParkAPI; +import nl.iobyte.themepark.api.config.objects.Configuration; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.load.interfaces.IDataLoader; +import nl.iobyte.themeparkconnector.api.operator.OperatorService; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItem; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItemState; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorPanel; +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; +import org.bukkit.configuration.ConfigurationSection; +import java.io.File; +import java.util.LinkedHashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class OperatorDataLoader implements IDataLoader { + + /** + * Called when loading data + */ + public void load() { + ThemeParkAPI api = ThemePark.getInstance().getAPI(); + + File folder = new File(ThemeParkConnector.getInstance().getDataFolder(), "panels"); + if(!folder.exists()) { + if (!folder.mkdirs()) { + ThemeParkConnectorLogger.toConsole("Unable to create 'panels' folder"); + return; + } + } + + Set keys = new HashSet<>(api.getAttractionService().getRegions().keySet()); + if(!new File(folder, "templates").exists()) { + keys.add("templates"); + } else { + loadPanels(api, "panels/templates"); + } + + File[] files = folder.listFiles(); + if(files != null && files.length != 0) { + for (File file : files) { + String name = file.getName(); + if (name.equals("templates")) + continue; + + if (file.isFile()) { + ThemeParkConnectorLogger.toConsole("File '" + name + "' in 'panels' folder not allowed"); + if (!file.delete()) + ThemeParkConnectorLogger.toConsole("Unable to delete file '" + name + "' in 'panels' folder"); + + continue; + } + + if (!api.getAttractionService().hasRegion(name)) { + ThemeParkConnectorLogger.toConsole("Folder '" + name + "' in 'panels' folder not allowed"); + if (!file.delete()) + ThemeParkConnectorLogger.toConsole("Unable to delete folder '" + name + "' in 'panels' folder"); + + continue; + } + + keys.remove(name); + loadPanels(api, "panels/"+name); + } + } + + for(String name : keys) { + if (!new File(ThemeParkConnector.getInstance().getDataFolder(), "panels/" + name).mkdir()) { + ThemeParkConnectorLogger.toConsole("Unable to create folder '" + name + "' in 'panels' folder"); + continue; + } + + loadPanels(api, "panels/"+name); + } + + for(String id : ThemeParkConnector.getInstance().getAPI().getOperatorService().getPanels().keySet()) { + if(api.getAttractionService().hasAttraction(id)) + continue; + + ThemeParkConnector.getInstance().getAPI().getOperatorService().removePanel(id); + } + } + + private void loadPanels(ThemeParkAPI api, String path) { + File folder = new File(ThemeParkConnector.getInstance().getDataFolder(), path); + File[] files = folder.listFiles(); + + Set keys; + if(folder.getName().equals("templates")) { + keys = new HashSet<>(); + } else { + keys = new HashSet<>(api.getAttractionService().getRegion(folder.getName()).getAttractions().keySet()); + } + + if(files != null && files.length != 0) { + for (File file : files) { + String name = file.getName(); + if (!file.isFile()) { + ThemeParkConnectorLogger.toConsole("Folder '" + name + "' in '" + path + "' folder not allowed"); + if (!file.delete()) + ThemeParkConnectorLogger.toConsole("Unable to delete folder '" + name + "' in 'panels' folder"); + + continue; + } + + if (!name.endsWith(".yml") && !name.endsWith(".json")) { + ThemeParkConnectorLogger.toConsole("Extension of file '" + name + "' in '" + path + "' folder not allowed"); + continue; + } + + keys.remove(name.replaceAll("(.json|.yml)", "")); + loadPanel(api, path, name); + } + } + + for(String name : keys) + loadPanel(api, path, name+".yml"); + } + + private void loadPanel(ThemeParkAPI api, String path, String id) { + Configuration configuration = new Configuration(ThemeParkConnector.getInstance(), path+"/"+id, false); + id = id.replaceAll("(.json|.yml)", ""); + if(configuration.contains("template")) { + loadTemplatedPanel(ThemeParkConnector.getInstance().getAPI().getOperatorService(), configuration, id); + if(!api.getAttractionService().hasAttraction(id) || !ThemeParkConnector.getInstance().getAPI().getOperatorService().hasPanel(id)) + return; + + String permission = configuration.getString("permission"); + String start_command = configuration.getString("start_command"); + String stop_command = configuration.getString("stop_command"); + ThemeParkConnector.getInstance().getAPI().getOperatorService().addOperator(new AttractionOperator( + id, + permission, + start_command, + stop_command + )); + return; + } + + if(!configuration.contains("items")) + return; + + ConfigurationSection items = configuration.getSection("items"); + if(items == null || items.getKeys(false).isEmpty()) + return; + + ConfigurationSection states; + OperatorPanel panel = new OperatorPanel(id); + for(String item_id : items.getKeys(false)) { + path = "items."+item_id; + String name = configuration.getString(path+".name"); + String active_state = configuration.getString(path+".active_state"); + if(!configuration.contains(path+".states")) + continue; + + states = configuration.getSection(path+".states"); + if(states == null || states.getKeys(false).isEmpty()) + continue; + + OperatorItem item = new OperatorItem(item_id, name, active_state); + for(String state_id : states.getKeys(false)) { + path = "items." + item_id + ".states." + state_id; + name = configuration.getString(path+".name"); + String cover = configuration.getString(path+".cover"); + String command = configuration.getString(path+".command"); + + String text_color = configuration.getString(path+".text_color"); + String background_color = configuration.getString(path+".background_color"); + if(text_color != null && !text_color.isEmpty() && background_color != null && !background_color.isEmpty()) { + boolean glow = configuration.getBoolean(path+".glow"); + + item.addState(new OperatorItemState(state_id, name, cover, command, text_color, background_color, glow)); + continue; + } + + item.addState(new OperatorItemState(state_id, name, cover, command)); + } + + panel.addItem(item); + } + + if(panel.getItems().size() == 0) + return; + + ThemeParkConnector.getInstance().getAPI().getOperatorService().addPanel(panel); + if(!api.getAttractionService().hasAttraction(id)) + return; + + String permission = configuration.getString("permission"); + String start_command = configuration.getString("start_command"); + String stop_command = configuration.getString("stop_command"); + ThemeParkConnector.getInstance().getAPI().getOperatorService().addOperator(new AttractionOperator( + id, + permission, + start_command, + stop_command + )); + } + + private void loadTemplatedPanel(OperatorService service, Configuration configuration, String id) { + OperatorPanel panel = service.getPanel(configuration.getString("template")); + if(panel == null) + return; + + if(!configuration.contains("items")) + return; + + ConfigurationSection items = configuration.getSection("items"); + if(items == null || items.getKeys(false).isEmpty()) + return; + + String path; + Map map; + ConfigurationSection states; + Map> commandMap = new LinkedHashMap<>(); + for(String item_id : items.getKeys(false)) { + path = "items."+item_id; + if(!configuration.contains(path+".states")) + continue; + + states = configuration.getSection("states"); + if(states == null || states.getKeys(false).isEmpty()) + continue; + + map = new LinkedHashMap<>(); + for(String state_id : items.getKeys(false)) { + path = "items." + item_id + ".states." + state_id; + String command = configuration.getString(path+".command"); + + map.put(state_id, command); + } + + if(map.size() == 0) + continue; + + commandMap.put(item_id, map); + } + + if(commandMap.size() == 0) + return; + + service.addPanel(panel.copy(id, commandMap)); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/message/MessageKey.java b/src/main/java/nl/iobyte/themeparkconnector/api/message/MessageKey.java new file mode 100644 index 0000000..8ae7c79 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/message/MessageKey.java @@ -0,0 +1,56 @@ +package nl.iobyte.themeparkconnector.api.message; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.config.enums.StorageLocation; +import org.bukkit.entity.Player; + +public enum MessageKey { + + //Prefix + PREFIX("prefix"), + + //Session + CLIENT_UNABLE_TO_CONNECT("client.connect.unable"), + CLIENT_GENERATE_TO_CONNECT("client.connect.generate"), + CLIENT_CLICK_TO_CONNECT("client.connect.click"), + CLIENT_HOVER_TO_CONNECT("client.connect.hover"), + + //Connection + CLIENT_CONNECTION_EXISTS("client.connection.exists"), + CLIENT_CONNECTION_OPEN("client.connection.open"), + CLIENT_CONNECTION_CLOSED("client.connection.closed"); + + private final String path; + MessageKey(String path) { + this.path = path; + } + + /** + * Get path to message + * @return String + */ + public String getPath() { + return path; + } + + /** + * Get and color message from config + * @return String + */ + public String getMessage() { + String msg = Text.color(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageLocation.MESSAGE, path)); + if(this != MessageKey.PREFIX) + msg = msg.replace("{prefix}", MessageKey.PREFIX.getMessage()); + + return msg; + } + + /** + * Send message with color to Player + * @param player Player + */ + public void send(Player player) { + player.sendMessage(getMessage()); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/message/Text.java b/src/main/java/nl/iobyte/themeparkconnector/api/message/Text.java new file mode 100644 index 0000000..cf94064 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/message/Text.java @@ -0,0 +1,25 @@ +package nl.iobyte.themeparkconnector.api.message; + +import org.bukkit.ChatColor; + +public class Text { + + /** + * Color text + * @param str String + * @return String + */ + public static String color(String str) { + return ChatColor.translateAlternateColorCodes('&', str); + } + + /** + * Strip text of color + * @param str String + * @return String + */ + public static String strip(String str) { + return ChatColor.stripColor(color(str)); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/NetworkingService.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/NetworkingService.java new file mode 100644 index 0000000..e08863e --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/NetworkingService.java @@ -0,0 +1,152 @@ +package nl.iobyte.themeparkconnector.api.network; + +import nl.iobyte.themepark.scheduler.ThemeParkScheduler; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.network.handlers.ClientConnectivityHandler; +import nl.iobyte.themeparkconnector.api.network.handlers.ClientOperatorHandler; +import nl.iobyte.themeparkconnector.api.network.io.SocketConnector; +import nl.iobyte.themeparkconnector.api.network.objects.INetworkingEvent; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; +import nl.iobyte.themeparkconnector.api.packet.objects.PayloadHandler; +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import java.util.*; + +public class NetworkingService { + + private final Set eventHandlers = new HashSet<>(); + private final Map clientMap = new HashMap<>(); + private final Map>> packetHandlerMap = new HashMap<>(); + private SocketConnector socketConnector; + + /** + * Initialize SocketConnector + */ + public void init() { + if(socketConnector != null) + return; + + socketConnector = new SocketConnector(); + + //Connect, Disconnect, Kick + ClientConnectivityHandler.load(this); + + //Operator + ClientOperatorHandler.load(this); + } + + /** + * Establish a socket connection if the connection is down + */ + public void connectIfDown() { + ThemeParkScheduler.runAsync(() -> socketConnector.setupConnection()); + } + + public void send(ClientConnection client, AbstractPacket packet) { + for (INetworkingEvent event : getEvents()) + event.onPacketSend(client, packet); + + socketConnector.send(client, packet); + } + + /** + * Get handler for packet and call it + * @param abstractPacket Received Packet + */ + public void triggerPacket(AbstractPacket abstractPacket) { + if (packetHandlerMap.get(abstractPacket.getChannel()) == null) { + ThemeParkConnectorLogger.toConsole("Unknown handler for packet channel " + abstractPacket.getChannel()); + return; + } + + packetHandlerMap.get(abstractPacket.getChannel()).forEach(handler -> handler.handle(abstractPacket)); + } + + public void registerHandler(PacketChannel channel, PayloadHandler handler) { + registerHandler(channel.toString(), handler); + } + + /** + * Link a handler to a packet type + * @param channel Channel of the packet + * @param handler Handler for the incoming data + */ + public void registerHandler(String channel, PayloadHandler handler) { + List> handlers = packetHandlerMap.getOrDefault(channel, new ArrayList<>()); + handlers.add(handler); + packetHandlerMap.put(channel, handlers); + } + + /** + * Get Client from UUID + * @param uuid UUID of client + * @return ClientConnection instance + */ + public ClientConnection getClient(UUID uuid) { + if (clientMap.containsKey(uuid)) + return clientMap.get(uuid); + + Player player = Bukkit.getPlayer(uuid); + if (player == null) + return null; + + return register(player); + } + + /** + * Returns all ClientConnections + * @return Collection of all ClientConnections + */ + public Collection getClients() { + return clientMap.values(); + } + + /** + * Remove client from the NetworkingService + * @param player Player's UUID + */ + public void remove(UUID player) { + if (!clientMap.containsKey(player)) + return; + + clientMap.remove(player).remove(); + } + + /** + * Register client to the NetworkingService + * @param player Player + * @return ClientConnection instance + */ + public ClientConnection register(Player player) { + ClientConnection clientConnection = new ClientConnection(player); + clientMap.put(player.getUniqueId(), clientConnection); + return clientConnection; + } + + /** + * Force the socket connection to close + */ + public void stop() { + socketConnector.disconnect(); + } + + /** + * Get networking events + * @return Set of NetworkingEvents + */ + public Set getEvents() { + return eventHandlers; + } + + /** + * Add event handler for networking activity + * @param events Networking event + */ + public void addEventHandler(INetworkingEvent events) { + eventHandlers.add(events); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/drivers/ClientDriver.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/drivers/ClientDriver.java new file mode 100644 index 0000000..d76dc5c --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/drivers/ClientDriver.java @@ -0,0 +1,29 @@ +package nl.iobyte.themeparkconnector.api.network.drivers; + +import io.socket.client.Socket; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.network.io.SocketConnector; +import nl.iobyte.themeparkconnector.api.network.objects.SocketDriver; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; + +public class ClientDriver extends SocketDriver { + + /** + * Register event to handle incoming packets + * @param socket Socket + * @param connector SocketConnector + */ + public void boot(Socket socket, SocketConnector connector) { + socket.on("data", args -> { + try { + AbstractPacket packet = ThemeParkConnector.getGson().fromJson(args[0].toString(), AbstractPacket.class); + ThemeParkConnector.getInstance().getAPI().getNetworkingService().triggerPacket(packet); + } catch (Exception e) { + ThemeParkConnectorLogger.toConsole("Failed to parse incoming packet. The received data was: " + args[0].toString()); + e.printStackTrace(); + } + }); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/drivers/SystemDriver.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/drivers/SystemDriver.java new file mode 100644 index 0000000..32e0c20 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/drivers/SystemDriver.java @@ -0,0 +1,85 @@ +package nl.iobyte.themeparkconnector.api.network.drivers; + +import io.socket.client.Manager; +import io.socket.client.Socket; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.message.MessageKey; +import nl.iobyte.themeparkconnector.api.network.io.SocketConnector; +import nl.iobyte.themeparkconnector.api.network.objects.SocketDriver; +import nl.iobyte.themeparkconnector.api.state.states.ConnectedState; +import nl.iobyte.themeparkconnector.api.state.states.IdleState; +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; + +public class SystemDriver extends SocketDriver { + + /** + * Register what to do whit connection + * @param socket Socket + * @param connector SocketConnector + */ + public void boot(Socket socket, SocketConnector connector) { + //What to do when connecting + socket.on(Socket.EVENT_CONNECT, args -> { + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + api.getStateService().setState(new ConnectedState()); + api.getPlayerStateService().start(); + + for(ClientConnection client : ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClients()) { + if(!client.isWaitingForToken()) + continue; + + client.publishURL(); + } + }); + + //What to do when losing connection + socket.on(Socket.EVENT_DISCONNECT, args -> { + if(ThemeParkConnector.getInstance() == null || ThemeParkConnector.getInstance().isDisabling()) + return; + + cancelWaitForToken(); + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + if(!(api.getStateService().getCurrentState() instanceof IdleState)) + api.getStateService().setState(new IdleState("Disconnected from socket")); + + api.getPlayerStateService().stop(); + for(ClientConnection client : ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClients()) { + if(!client.isConnected()) + continue; + + client.onDisconnect(); + } + }); + + //What to do when can't connect + socket.on(Socket.EVENT_CONNECT_ERROR, args -> { + cancelWaitForToken(); + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + api.getStateService().setState(new IdleState("Socket connection error")); + api.getPlayerStateService().stop(); + + ThemeParkConnectorLogger.toConsole(ThemeParkConnector.getGson().toJson(args)); + }); + + socket.on(Manager.EVENT_ERROR, args -> ThemeParkConnectorLogger.toConsole(ThemeParkConnector.getGson().toJson(args))); + } + + /** + * Cancel tokens for all people waiting for one + */ + private void cancelWaitForToken() { + if(ThemeParkConnector.getInstance() == null) + return; + + for(ClientConnection client : ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClients()) { + if(!client.isWaitingForToken()) + continue; + + client.setWaitingForToken(false); + MessageKey.CLIENT_UNABLE_TO_CONNECT.send(client.getPlayer()); + } + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/handlers/ClientConnectivityHandler.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/handlers/ClientConnectivityHandler.java new file mode 100644 index 0000000..92a4013 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/handlers/ClientConnectivityHandler.java @@ -0,0 +1,15 @@ +package nl.iobyte.themeparkconnector.api.network.handlers; + +import nl.iobyte.themeparkconnector.api.network.NetworkingService; +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.handlers.ClientConnectHandler; +import nl.iobyte.themeparkconnector.api.packet.handlers.ClientDisconnectHandler; + +public class ClientConnectivityHandler { + + public static void load(NetworkingService service) { + service.registerHandler(PacketChannel.SERVER_IN_REGISTER_CLIENT, new ClientConnectHandler()); + service.registerHandler(PacketChannel.SERVER_IN_UNREGISTER_CLIENT, new ClientDisconnectHandler()); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/handlers/ClientOperatorHandler.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/handlers/ClientOperatorHandler.java new file mode 100644 index 0000000..be8aac2 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/handlers/ClientOperatorHandler.java @@ -0,0 +1,13 @@ +package nl.iobyte.themeparkconnector.api.network.handlers; + +import nl.iobyte.themeparkconnector.api.network.NetworkingService; +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.handlers.operator.ClientOperatorClickHandler; + +public class ClientOperatorHandler { + + public static void load(NetworkingService service) { + service.registerHandler(PacketChannel.SERVER_IN_OPERATOR_CLICK, new ClientOperatorClickHandler()); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/io/NullProxySelector.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/io/NullProxySelector.java new file mode 100644 index 0000000..e17d99b --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/io/NullProxySelector.java @@ -0,0 +1,26 @@ +package nl.iobyte.themeparkconnector.api.network.io; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.Collections; +import java.util.List; + +public class NullProxySelector extends ProxySelector { + + @Override + public List select(URI uri) { + if (uri == null) { + throw new IllegalArgumentException("uri must not be null"); + } + return Collections.singletonList(Proxy.NO_PROXY); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + ioe.printStackTrace(); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/io/SocketConnector.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/io/SocketConnector.java new file mode 100644 index 0000000..da846b9 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/io/SocketConnector.java @@ -0,0 +1,122 @@ +package nl.iobyte.themeparkconnector.api.network.io; + +import io.socket.client.IO; +import io.socket.client.Socket; +import nl.iobyte.themepark.scheduler.ThemeParkScheduler; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.config.enums.StorageKey; +import nl.iobyte.themeparkconnector.api.message.MessageKey; +import nl.iobyte.themeparkconnector.api.network.drivers.ClientDriver; +import nl.iobyte.themeparkconnector.api.network.drivers.SystemDriver; +import nl.iobyte.themeparkconnector.api.network.objects.CertificateHelper; +import nl.iobyte.themeparkconnector.api.network.objects.SocketDriver; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; +import nl.iobyte.themeparkconnector.api.state.states.ConnectingState; +import nl.iobyte.themeparkconnector.api.state.states.IdleState; +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; +import okhttp3.OkHttpClient; +import java.net.URISyntaxException; + +public class SocketConnector { + + private static final SocketDriver[] drivers = { + new SystemDriver(), + new ClientDriver(), + }; + + private Socket socket; + + /** + * Try to establish connection to the ThemeParkConnector network + */ + public void setupConnection() { + if(!ThemeParkConnector.getInstance().getAPI().getStateService().getCurrentState().canConnect()) + return; + + //Preparing socket.io settings + OkHttpClient okHttpClient = CertificateHelper.ignore(new OkHttpClient.Builder().proxySelector(new NullProxySelector())).build(); + + IO.Options opts = new IO.Options(); + opts.callFactory = okHttpClient; + opts.reconnection = false; + opts.webSocketFactory = okHttpClient; + + // authentication headers + opts.query = String.format( + "type=server&id=%s", + ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SOCKET_ID) + ); + + //Setup socket.io connection + try { + socket = IO.socket( + ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SOCKET_URL), + opts + ); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + + //Register state to be connecting + ThemeParkConnector.getInstance().getAPI().getStateService().setState(new ConnectingState()); + + //Schedule timeout check + ThemeParkScheduler.runSyncLater(() -> { + if (!(ThemeParkConnector.getInstance().getAPI().getStateService().getCurrentState() instanceof ConnectingState)) + return; + + cancelWaitingForToken(); + ThemeParkConnectorLogger.toConsole("Connecting timed out."); + ThemeParkConnector.getInstance().getAPI().getStateService().setState(new IdleState("Connecting to the socket timed out")); + }, 20 * 35); + + + //Register drivers + for(SocketDriver driver : drivers) + driver.boot(socket, this); + + socket.connect(); + } + + /** + * Close connection with Socket + */ + public void disconnect() { + if(socket != null) + socket.disconnect(); + } + + /** + * Send Packet + * @param client ClientConnection + * @param packet AbstractPacket + */ + public void send(ClientConnection client, AbstractPacket packet) { + if(client == null || packet == null) + return; + + if (!ThemeParkConnector.getInstance().getAPI().getStateService().isConnected()) + return; + + if(!client.isConnected()) + return; + + String data = client.getUsedToken() + "@" + ThemeParkConnector.getGson().toJson(packet); + socket.emit("data", data); + } + + /** + * Cancel tokens for all people waiting for one + */ + public void cancelWaitingForToken() { + for(ClientConnection client : ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClients()) { + if(!client.isWaitingForToken()) + continue; + + client.setWaitingForToken(false); + MessageKey.CLIENT_UNABLE_TO_CONNECT.send(client.getPlayer()); + } + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/CertificateHelper.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/CertificateHelper.java new file mode 100644 index 0000000..3bad4c7 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/CertificateHelper.java @@ -0,0 +1,43 @@ +package nl.iobyte.themeparkconnector.api.network.objects; + +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; +import okhttp3.OkHttpClient; + +import javax.net.ssl.*; +import java.security.cert.CertificateException; + +public class CertificateHelper { + + public static OkHttpClient.Builder ignore(OkHttpClient.Builder builder) { + try { + // Create a trust manager that does not validate certificate chains + final TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) { } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) { } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + } + }; + + // Install the all-trusting trust manager + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, trustAllCerts, new java.security.SecureRandom()); + // Create an ssl socket factory with our all-trusting manager + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + builder.sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]); + builder.hostnameVerifier((hostname, session) -> true); + } catch (Exception e) { + ThemeParkConnectorLogger.toConsole("Failed to middleman ssl, should probably be fine"); + } + return builder; + } + +} \ No newline at end of file diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/INetworkingEvent.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/INetworkingEvent.java new file mode 100644 index 0000000..cf15156 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/INetworkingEvent.java @@ -0,0 +1,27 @@ +package nl.iobyte.themeparkconnector.api.network.objects; + +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; + +public interface INetworkingEvent { + + /** + * Event to call when packet is being send + * @param client ClientConnection + * @param packet AbstractPacket + */ + default void onPacketSend(ClientConnection client, AbstractPacket packet) {} + + /** + * Event to call when the client opens the connection + * @param client ClientConnection + */ + default void onClientOpen(ClientConnection client) {} + + /** + * Event to call when the client closes the connection + * @param client ClientConnection + */ + default void onClientClose(ClientConnection client) {} + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/SocketDriver.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/SocketDriver.java new file mode 100644 index 0000000..8281a8d --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/objects/SocketDriver.java @@ -0,0 +1,15 @@ +package nl.iobyte.themeparkconnector.api.network.objects; + +import io.socket.client.Socket; +import nl.iobyte.themeparkconnector.api.network.io.SocketConnector; + +public abstract class SocketDriver { + + /** + * Called to register stuff to the socket/connector + * @param socket Socket + * @param connector SocketConnector + */ + public abstract void boot(Socket socket, SocketConnector connector); + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/KickClientPacket.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/KickClientPacket.java new file mode 100644 index 0000000..0241bb1 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/KickClientPacket.java @@ -0,0 +1,12 @@ +package nl.iobyte.themeparkconnector.api.network.packet; + +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; + +public class KickClientPacket extends AbstractPacket { + + public KickClientPacket() { + super(PacketChannel.CLIENT_KICK, null); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/NotificationPacket.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/NotificationPacket.java new file mode 100644 index 0000000..ff8f7fe --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/NotificationPacket.java @@ -0,0 +1,14 @@ +package nl.iobyte.themeparkconnector.api.network.packet; + +import nl.iobyte.themeparkconnector.api.client.objects.Notification; +import nl.iobyte.themeparkconnector.api.network.payload.NotificationPayload; +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; + +public class NotificationPacket extends AbstractPacket { + + public NotificationPacket(Notification notification) { + super(PacketChannel.CLIENT_NOTIFICATION, new NotificationPayload(notification)); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/OperatorRequestReplyPacket.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/OperatorRequestReplyPacket.java new file mode 100644 index 0000000..e6e463b --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/OperatorRequestReplyPacket.java @@ -0,0 +1,26 @@ +package nl.iobyte.themeparkconnector.api.network.packet.operator; + +import nl.iobyte.themeparkconnector.api.operator.enums.OperatorRequestReply; +import nl.iobyte.themeparkconnector.api.network.payload.operator.OperatorRequestReplyPayload; +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; + +public class OperatorRequestReplyPacket extends AbstractPacket { + + public OperatorRequestReplyPacket(OperatorRequestReply requestReply, String message) { + super(PacketChannel.CLIENT_OPERATOR_REQUEST_REPLY, new OperatorRequestReplyPayload( + requestReply, + message, + null + )); + } + + public OperatorRequestReplyPacket(OperatorRequestReply requestReply, String message, Object data) { + super(PacketChannel.CLIENT_OPERATOR_REQUEST_REPLY, new OperatorRequestReplyPayload( + requestReply, + message, + data + )); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/OperatorStatePacket.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/OperatorStatePacket.java new file mode 100644 index 0000000..8df2705 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/OperatorStatePacket.java @@ -0,0 +1,18 @@ +package nl.iobyte.themeparkconnector.api.network.packet.operator; + +import nl.iobyte.themeparkconnector.api.network.payload.operator.OperatorStatePayload; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItemState; +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; + +public class OperatorStatePacket extends AbstractPacket { + + public OperatorStatePacket(String item_id, OperatorItemState state, String data) { + super(PacketChannel.CLIENT_OPERATOR_STATE, new OperatorStatePayload( + item_id, + state, + data + )); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/StatusChangePacket.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/StatusChangePacket.java new file mode 100644 index 0000000..14663ae --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/packet/operator/StatusChangePacket.java @@ -0,0 +1,18 @@ +package nl.iobyte.themeparkconnector.api.network.packet.operator; + +import nl.iobyte.themepark.api.attraction.enums.Status; +import nl.iobyte.themeparkconnector.api.network.payload.operator.OperatorStatePayload; +import nl.iobyte.themeparkconnector.api.network.payload.operator.StatusChangePayload; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItemState; +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacket; + +public class StatusChangePacket extends AbstractPacket { + + public StatusChangePacket(Status status) { + super(PacketChannel.CLIENT_ATTRACTION_STATE, new StatusChangePayload( + status + )); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/NotificationPayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/NotificationPayload.java new file mode 100644 index 0000000..733c3b1 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/NotificationPayload.java @@ -0,0 +1,15 @@ +package nl.iobyte.themeparkconnector.api.network.payload; + +import nl.iobyte.themeparkconnector.api.client.objects.Notification; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; + +public class NotificationPayload extends AbstractPacketPayload { + + private String title, message; + + public NotificationPayload(Notification notification) { + this.title = notification.getTitle(); + this.message = notification.getMessage(); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/OperatorRequestReplyPayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/OperatorRequestReplyPayload.java new file mode 100644 index 0000000..1c51edb --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/OperatorRequestReplyPayload.java @@ -0,0 +1,18 @@ +package nl.iobyte.themeparkconnector.api.network.payload.operator; + +import nl.iobyte.themeparkconnector.api.operator.enums.OperatorRequestReply; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; + +public class OperatorRequestReplyPayload extends AbstractPacketPayload { + + private OperatorRequestReply requestReply; + private String message; + private Object data; + + public OperatorRequestReplyPayload(OperatorRequestReply requestReply, String message, Object data) { + this.requestReply = requestReply; + this.message = message; + this.data = data; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/OperatorStatePayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/OperatorStatePayload.java new file mode 100644 index 0000000..3bdf69a --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/OperatorStatePayload.java @@ -0,0 +1,18 @@ +package nl.iobyte.themeparkconnector.api.network.payload.operator; + +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItemState; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; + +public class OperatorStatePayload extends AbstractPacketPayload { + + private String item_id; + private OperatorItemState state; + private String data; + + public OperatorStatePayload(String item_id, OperatorItemState state, String data) { + this.item_id = item_id; + this.state = state; + this.data = data; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/StatusChangePayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/StatusChangePayload.java new file mode 100644 index 0000000..d066740 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/network/payload/operator/StatusChangePayload.java @@ -0,0 +1,14 @@ +package nl.iobyte.themeparkconnector.api.network.payload.operator; + +import nl.iobyte.themepark.api.attraction.enums.Status; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; + +public class StatusChangePayload extends AbstractPacketPayload { + + private Status status; + + public StatusChangePayload(Status status) { + this.status = status; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/OperatorService.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/OperatorService.java new file mode 100644 index 0000000..619d92e --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/OperatorService.java @@ -0,0 +1,89 @@ +package nl.iobyte.themeparkconnector.api.operator; + +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorPanel; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class OperatorService { + + private final Map operators = new HashMap<>(); + private final Map panels = new HashMap<>(); + private final Map operating = new HashMap<>(); + + public void addOperator(AttractionOperator operator) { + if(operator == null) + return; + + operators.put(operator.getAttractionID(), operator); + } + + public boolean hasOperator(String id) { + return operators.containsKey(id); + } + + public AttractionOperator getOperator(String id) { + return operators.get(id); + } + + public Map getOperators() { + return operators; + } + + public void addPanel(OperatorPanel panel) { + if(panel == null) + return; + + panels.put(panel.getID(), panel); + } + + public boolean hasPanel(String id) { + return panels.containsKey(id); + } + + public OperatorPanel getPanel(String id) { + return panels.get(id); + } + + public Map getPanels() { + return panels; + } + + public void removePanel(String id) { + panels.remove(id); + } + + public void startOperating(UUID uuid, String id) { + AttractionOperator operator = getOperator(id); + if(operator == null) + return; + + if(!operator.isAvailable()) + return; + + operator.setClient(uuid); + operating.put(uuid, id); + } + + public boolean isOperating(UUID uuid) { + return operating.containsKey(uuid); + } + + public void stopOperating(UUID uuid) { + String id = operating.remove(uuid); + if(id == null) + return; + + AttractionOperator operator = getOperator(id); + if(operator == null) + return; + + operator.setClient(null); + } + + public Map getOperating() { + return operating; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/adapter/OperatorItemAdapter.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/adapter/OperatorItemAdapter.java new file mode 100644 index 0000000..8c61cbc --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/adapter/OperatorItemAdapter.java @@ -0,0 +1,29 @@ +package nl.iobyte.themeparkconnector.api.operator.adapter; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItem; +import java.lang.reflect.Type; + +public class OperatorItemAdapter implements JsonSerializer { + + /** + * Serializes OperatorItem to json + * @param src OperatorItem + * @param typeOfSrc Type + * @param context JsonSerializationContext + * @return JsonElement + */ + @Override + public JsonElement serialize(OperatorItem src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject result = new JsonObject(); + result.add("id", context.serialize(src.getID())); + result.add("name", context.serialize(src.getName())); + result.add("state", context.serialize(src.getActiveState())); + + return result; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/adapter/OperatorPanelAdapter.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/adapter/OperatorPanelAdapter.java new file mode 100644 index 0000000..c919608 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/adapter/OperatorPanelAdapter.java @@ -0,0 +1,28 @@ +package nl.iobyte.themeparkconnector.api.operator.adapter; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorPanel; +import java.lang.reflect.Type; + +public class OperatorPanelAdapter implements JsonSerializer { + + /** + * Serializes OperatorPanel to json + * @param src OperatorPanel + * @param typeOfSrc Type + * @param context JsonSerializationContext + * @return JsonElement + */ + @Override + public JsonElement serialize(OperatorPanel src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject result = new JsonObject(); + result.add("id", context.serialize(src.getID())); + result.add("items", context.serialize(src.getItems().values())); + + return result; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/enums/OperatorRequestReply.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/enums/OperatorRequestReply.java new file mode 100644 index 0000000..cee8e13 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/enums/OperatorRequestReply.java @@ -0,0 +1,8 @@ +package nl.iobyte.themeparkconnector.api.operator.enums; + +public enum OperatorRequestReply { + + ACCEPT, + DENY + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/AttractionOperator.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/AttractionOperator.java new file mode 100644 index 0000000..0b798ac --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/AttractionOperator.java @@ -0,0 +1,102 @@ +package nl.iobyte.themeparkconnector.api.operator.objects; + +import nl.iobyte.themepark.ThemePark; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themepark.api.attraction.objects.Attraction; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.event.operator.OperatorConnectEvent; +import nl.iobyte.themeparkconnector.api.event.operator.OperatorDisconnectEvent; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorPanel; +import java.util.UUID; + +public class AttractionOperator { + + private UUID client_uuid; + private final String attraction_id, permission, start_command, stop_command; + + public AttractionOperator(Attraction attraction, String permission, String start_command, String stop_command) { + this.attraction_id = attraction.getID(); + this.permission = permission; + this.start_command = start_command; + this.stop_command = stop_command; + } + + public AttractionOperator(String attraction_id, String permission, String start_command, String stop_command) { + this.attraction_id = attraction_id; + this.permission = permission; + this.start_command = start_command; + this.stop_command = stop_command; + } + + public UUID getClientUUID() { + return client_uuid; + } + + public ClientConnection getClientConnection() { + return ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClient(client_uuid); + } + + public boolean canClientConnect(ClientConnection connection) { + if(connection == null) + return false; + + if(client_uuid != null) + return false; + + if(hasPermission()) + return connection.getPlayer().hasPermission(permission); + + return true; + } + + public void setClient(UUID client_uuid) { + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + if(client_uuid == null) { + api.getEventDispatcher().call(new OperatorDisconnectEvent( + this, + api.getNetworkingService().getClient(this.client_uuid) + )); + } else { + api.getEventDispatcher().call(new OperatorConnectEvent( + this, + api.getNetworkingService().getClient(client_uuid) + )); + } + + this.client_uuid = client_uuid; + } + + public String getAttractionID() { + return attraction_id; + } + + public Attraction getAttraction() { + return ThemePark.getInstance().getAPI().getAttractionService().getAttraction(attraction_id); + } + + public String getPermission() { + return permission; + } + + public boolean hasPermission() { + return permission != null && !permission.isEmpty(); + } + + public String getStartCommand() { + return start_command; + } + + public String getStopCommand() { + return stop_command; + } + + public boolean isAvailable() { + return client_uuid == null; + } + + public OperatorPanel getPanel() { + return ThemeParkConnector.getInstance().getAPI().getOperatorService().getPanel(attraction_id); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorItem.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorItem.java new file mode 100644 index 0000000..d7524f9 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorItem.java @@ -0,0 +1,84 @@ +package nl.iobyte.themeparkconnector.api.operator.objects.panel; + +import nl.iobyte.themepark.scheduler.ThemeParkScheduler; +import org.bukkit.Bukkit; +import java.util.HashMap; +import java.util.Map; + +public class OperatorItem { + + private final String id, name; + private final Map states; + private String active_state; + + public OperatorItem(String id, String name, String active_state) { + this.id = id; + this.name = name; + this.active_state = active_state; + this.states = new HashMap<>(); + } + + public OperatorItem(String id, String name, String active_state, Map states) { + this.id = id; + this.name = name; + this.active_state = active_state; + this.states = states; + } + + public String getID() { + return id; + } + + public String getName() { + return name; + } + + public OperatorItemState getActiveState() { + return getState(active_state); + } + + public void setActiveState(String active_state) { + this.active_state = active_state; + } + + public void addState(OperatorItemState state) { + if(hasState(state.getID())) + return; + + states.put(state.getID(), state); + } + + public boolean hasState(String id) { + return states.containsKey(id); + } + + public OperatorItemState getState(String id) { + return states.get(id); + } + + public void click() { + if(!states.containsKey(active_state)) + return; + + String cmd = getActiveState().getCommand(); + if(cmd == null || cmd.isEmpty()) + return; + + ThemeParkScheduler.runSync(() -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd)); + } + + public OperatorItem copy(Map commands) { + Map states = new HashMap<>(); + OperatorItemState state; + for(Map.Entry entrySet : this.states.entrySet()) { + state = entrySet.getValue(); + states.put( + entrySet.getKey(), + state.copy(commands.containsKey(entrySet.getKey()) ? commands.get(entrySet.getKey()) : state.getCommand()) + ); + } + + return new OperatorItem(id, name, active_state, states); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorItemState.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorItemState.java new file mode 100644 index 0000000..917c7e7 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorItemState.java @@ -0,0 +1,67 @@ +package nl.iobyte.themeparkconnector.api.operator.objects.panel; + +public class OperatorItemState { + + private final String id, name, cover, text_color, background_color; + private final transient String command; + private final boolean glow; + + public OperatorItemState(String id, String name, String cover, String command) { + if(command !=null && command.startsWith("/")) + command = command.replaceFirst("/", ""); + + this.id = id; + this.name = name; + this.cover = cover; + this.command = command; + this.text_color = "#fff"; + this.background_color = "#666"; + this.glow = false; + } + + public OperatorItemState(String id, String name, String cover, String command, String text_color, String background_color, boolean glow) { + if(command !=null && command.startsWith("/")) + command = command.replaceFirst("/", ""); + + this.id = id; + this.name = name; + this.cover = cover; + this.command = command; + this.text_color = text_color; + this.background_color = background_color; + this.glow = glow; + } + + public String getID() { + return id; + } + + public String getName() { + return name; + } + + public String getCover() { + return cover; + } + + public String getCommand() { + return command; + } + + public String getTextColor() { + return text_color; + } + + public String getBackgroundColor() { + return background_color; + } + + public boolean isGlowing() { + return glow; + } + + public OperatorItemState copy(String command) { + return new OperatorItemState(id, name, cover, command, text_color, background_color, glow); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorPanel.java b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorPanel.java new file mode 100644 index 0000000..c87ea6d --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/operator/objects/panel/OperatorPanel.java @@ -0,0 +1,71 @@ +package nl.iobyte.themeparkconnector.api.operator.objects.panel; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class OperatorPanel { + + private final String id; + private final Map items; + + public OperatorPanel(String id) { + this.id = id; + this.items = new LinkedHashMap<>(); + } + + public OperatorPanel(String id, Map items) { + this.id = id; + this.items = items; + } + + public String getID() { + return id; + } + + public void addItem(OperatorItem item) { + if(item == null) + return; + + if(hasItem(item.getID())) + return; + + items.put(item.getID(), item); + } + + public void addItems(List items) { + for(OperatorItem item : items) + addItem(item); + } + + public void addItems(Map items) { + this.items.putAll(items); + } + + public boolean hasItem(String id) { + return items.containsKey(id); + } + + public OperatorItem getItem(String id) { + return items.get(id); + } + + public Map getItems() { + return items; + } + + public OperatorPanel copy(String id, Map> commandMap) { + Map items = new LinkedHashMap<>(); + OperatorItem item; + for(Map.Entry entrySet : this.items.entrySet()) { + item = entrySet.getValue(); + items.put( + entrySet.getKey(), + item.copy(commandMap.containsKey(entrySet.getKey()) ? commandMap.get(entrySet.getKey()) : new LinkedHashMap<>()) + ); + } + + return new OperatorPanel(id, items); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/adapter/PacketPayloadAdapter.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/adapter/PacketPayloadAdapter.java new file mode 100644 index 0000000..b95d352 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/adapter/PacketPayloadAdapter.java @@ -0,0 +1,53 @@ +package nl.iobyte.themeparkconnector.api.packet.adapter; + +import com.google.gson.*; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; +import nl.iobyte.themeparkconnector.logger.ThemeParkConnectorLogger; +import java.lang.reflect.Type; + +public class PacketPayloadAdapter implements JsonSerializer, JsonDeserializer { + + /** + * Serializes AbstractPacketPayload to json + * @param src AbstractPacketPayload + * @param typeOfSrc Type + * @param context JsonSerializationContext + * @return JsonElement + */ + @Override + public JsonElement serialize(AbstractPacketPayload src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject result = new JsonObject(); + result.add("type", new JsonPrimitive(src.getClass().getName())); + result.add("payload", context.serialize(src, src.getClass())); + + return result; + } + + /** + * Deserializes AbstractPacketPayload to correct type + * @param element JsonElement + * @param typeOf Type + * @param context JsonDeserializationContext + * @return AbstractPacketPayload + * @throws JsonParseException Exception that might occur + */ + @Override + public AbstractPacketPayload deserialize(JsonElement element, Type typeOf, JsonDeserializationContext context) throws JsonParseException { + try { + JsonObject object = element.getAsJsonObject(); + String type = object.get("type").toString().replace("\"", ""); + JsonElement payload = object.get("payload"); + + if(!type.contains(".")) + type = "nl.iobyte.themeparkconnector.api.packet.payloads."+type; + + return context.deserialize(payload, Class.forName(type)); + } catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/adapter/StatusAdapter.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/adapter/StatusAdapter.java new file mode 100644 index 0000000..1d50de3 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/adapter/StatusAdapter.java @@ -0,0 +1,29 @@ +package nl.iobyte.themeparkconnector.api.packet.adapter; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import nl.iobyte.themepark.api.attraction.enums.Status; +import java.lang.reflect.Type; + +public class StatusAdapter implements JsonSerializer { + + /** + * Serializes Status to json + * @param src Status + * @param type Type + * @param context JsonSerializationContext + * @return JsonElement + */ + @Override + public JsonElement serialize(Status src, Type type, JsonSerializationContext context) { + JsonObject result = new JsonObject(); + result.add("id", context.serialize(src.getID())); + result.add("name", context.serialize(src.getName())); + result.add("hex", context.serialize(src.getHexColor())); + + return result; + } + +} \ No newline at end of file diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/enums/PacketChannel.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/enums/PacketChannel.java new file mode 100644 index 0000000..1fe287b --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/enums/PacketChannel.java @@ -0,0 +1,21 @@ +package nl.iobyte.themeparkconnector.api.packet.enums; + +public enum PacketChannel { + + //Server in + SERVER_IN_REGISTER_CLIENT, + SERVER_IN_UNREGISTER_CLIENT, + + //Server in Operator + SERVER_IN_OPERATOR_CLICK, + + //Client + CLIENT_NOTIFICATION, + CLIENT_KICK, + + //Client Operator + CLIENT_OPERATOR_REQUEST_REPLY, + CLIENT_OPERATOR_STATE, + CLIENT_ATTRACTION_STATE + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/ClientConnectHandler.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/ClientConnectHandler.java new file mode 100644 index 0000000..f806921 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/ClientConnectHandler.java @@ -0,0 +1,104 @@ +package nl.iobyte.themeparkconnector.api.packet.handlers; + +import nl.iobyte.themepark.ThemePark; +import nl.iobyte.themepark.api.attraction.objects.Attraction; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themeparkconnector.api.message.Text; +import nl.iobyte.themeparkconnector.api.network.objects.INetworkingEvent; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.network.packet.operator.OperatorRequestReplyPacket; +import nl.iobyte.themeparkconnector.api.operator.enums.OperatorRequestReply; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import nl.iobyte.themeparkconnector.api.packet.objects.PayloadHandler; +import nl.iobyte.themeparkconnector.api.packet.payloads.ClientConnectPayload; +import java.util.UUID; + +public class ClientConnectHandler extends PayloadHandler { + + /** + * Player established connection with ThemeParkConnector + * @param payload ClientConnectPayload + */ + public void onReceive(ClientConnectPayload payload) { + ClientConnection connection = ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClient(payload.getUUID()); + if(connection == null) + return; + + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + if(!api.getAuthenticationService().matchToken(payload.getUUID(), payload.getToken(), payload.getAttractionID())) { + denyRequest( + api, + payload.getUUID(), + "Invalid token used to connect" + ); + return; + } + + if(api.getOperatorService().isOperating(payload.getUUID())) { + denyRequest( + api, + payload.getUUID(), + "You're already operating an attraction" + ); + return; + } + + Attraction attraction = ThemePark.getInstance().getAPI().getAttractionService().getAttraction(payload.getAttractionID()); + if(attraction == null) { + denyRequest( + api, + payload.getUUID(), + "Unable to find requested attraction" + ); + return; + } + + AttractionOperator operator = api.getOperatorService().getOperator(payload.getAttractionID()); + if(operator == null) { + denyRequest( + api, + payload.getUUID(), + "No operator found for attraction: " + Text.strip(attraction.getName()) + ); + return; + } + + + if(!operator.canClientConnect(connection)) { + denyRequest( + api, + payload.getUUID(), + "You're not allowed to operate attraction: " + Text.strip(attraction.getName()) + ); + return; + } + + api.getOperatorService().startOperating(payload.getUUID(), payload.getAttractionID()); + api.getNetworkingService().send( + api.getNetworkingService().getClient(payload.getUUID()), + new OperatorRequestReplyPacket( + OperatorRequestReply.ACCEPT, + "You're now operating attraction: " + Text.strip(attraction.getName()), + operator.getPanel() + ) + ); + + connection.setUsedToken(payload.getToken()); + connection.onConnect(); + + for(INetworkingEvent event : ThemeParkConnector.getInstance().getAPI().getNetworkingService().getEvents()) + event.onClientOpen(connection); + } + + private void denyRequest(ThemeParkConnectorAPI api, UUID uuid, String message) { + api.getNetworkingService().send( + api.getNetworkingService().getClient(uuid), + new OperatorRequestReplyPacket( + OperatorRequestReply.DENY, + message + ) + ); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/ClientDisconnectHandler.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/ClientDisconnectHandler.java new file mode 100644 index 0000000..684d4d4 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/ClientDisconnectHandler.java @@ -0,0 +1,23 @@ +package nl.iobyte.themeparkconnector.api.packet.handlers; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.packet.objects.PayloadHandler; +import nl.iobyte.themeparkconnector.api.packet.payloads.ClientDisconnectPayload; + +public class ClientDisconnectHandler extends PayloadHandler { + + /** + * Player closed connection with ThemeParkConnector + * @param payload ClientDisconnectPayload + */ + public void onReceive(ClientDisconnectPayload payload) { + ClientConnection connection = ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClient(payload.getUUID()); + if(connection == null) + return; + + connection.onDisconnect(); + ThemeParkConnector.getInstance().getAPI().getOperatorService().stopOperating(payload.getUUID()); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/operator/ClientOperatorClickHandler.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/operator/ClientOperatorClickHandler.java new file mode 100644 index 0000000..4eafde6 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/handlers/operator/ClientOperatorClickHandler.java @@ -0,0 +1,40 @@ +package nl.iobyte.themeparkconnector.api.packet.handlers.operator; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.operator.OperatorService; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItem; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorPanel; +import nl.iobyte.themeparkconnector.api.packet.objects.PayloadHandler; +import nl.iobyte.themeparkconnector.api.packet.payloads.operator.ClientOperatorClickPayload; + +public class ClientOperatorClickHandler extends PayloadHandler { + + /** + * Player click ThemeParkConnector Ride Operator + * @param payload ClientOperatorClickPayload + */ + public void onReceive(ClientOperatorClickPayload payload) { + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + ClientConnection connection = api.getNetworkingService().getClient(payload.getUUID()); + if(connection == null) + return; + + OperatorService service = ThemeParkConnector.getInstance().getAPI().getOperatorService(); + String attraction_id = service.getOperating().get(payload.getUUID()); + if(attraction_id == null) + return; + + OperatorPanel panel = service.getPanel(attraction_id); + if(panel == null) + return; + + OperatorItem item = panel.getItem(payload.getItemID()); + if(item == null) + return; + + item.click(); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/AbstractPacket.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/AbstractPacket.java new file mode 100644 index 0000000..e010cef --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/AbstractPacket.java @@ -0,0 +1,35 @@ +package nl.iobyte.themeparkconnector.api.packet.objects; + +import nl.iobyte.themeparkconnector.api.packet.enums.PacketChannel; + +public class AbstractPacket { + + private String channel; + private AbstractPacketPayload data; + + public AbstractPacket(PacketChannel channel, AbstractPacketPayload data) { + this(channel.toString(), data); + } + + public AbstractPacket(String channel, AbstractPacketPayload data) { + this.channel = channel; + this.data = data; + } + + /** + * Get packet channel + * @return PacketChannel + */ + public String getChannel() { + return channel; + } + + /** + * Get payload data + * @return AbstractPacketPayload + */ + public AbstractPacketPayload getData() { + return data; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/AbstractPacketPayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/AbstractPacketPayload.java new file mode 100644 index 0000000..ebf9bc3 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/AbstractPacketPayload.java @@ -0,0 +1,3 @@ +package nl.iobyte.themeparkconnector.api.packet.objects; + +public class AbstractPacketPayload { } diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/PayloadHandler.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/PayloadHandler.java new file mode 100644 index 0000000..5c32e7c --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/objects/PayloadHandler.java @@ -0,0 +1,20 @@ +package nl.iobyte.themeparkconnector.api.packet.objects; + +public abstract class PayloadHandler { + + /** + * Handle incoming packet + * @param packet AbstractPacket + */ + @SuppressWarnings("unchecked cast") + public void handle(AbstractPacket packet) { + onReceive((E) packet.getData()); + } + + /** + * Called when receiving payload + * @param payload Class + */ + public abstract void onReceive(E payload); + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/ClientConnectPayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/ClientConnectPayload.java new file mode 100644 index 0000000..3c18f9e --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/ClientConnectPayload.java @@ -0,0 +1,24 @@ +package nl.iobyte.themeparkconnector.api.packet.payloads; + +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; +import java.util.UUID; + +public class ClientConnectPayload extends AbstractPacketPayload { + + private UUID uuid; + private String token; + private String attraction_id; + + public UUID getUUID() { + return uuid; + } + + public String getToken() { + return token; + } + + public String getAttractionID() { + return attraction_id; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/ClientDisconnectPayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/ClientDisconnectPayload.java new file mode 100644 index 0000000..cd3588c --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/ClientDisconnectPayload.java @@ -0,0 +1,15 @@ +package nl.iobyte.themeparkconnector.api.packet.payloads; + +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; + +import java.util.UUID; + +public class ClientDisconnectPayload extends AbstractPacketPayload { + + private UUID uuid; + + public UUID getUUID() { + return uuid; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/operator/ClientOperatorClickPayload.java b/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/operator/ClientOperatorClickPayload.java new file mode 100644 index 0000000..9b195e3 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/packet/payloads/operator/ClientOperatorClickPayload.java @@ -0,0 +1,19 @@ +package nl.iobyte.themeparkconnector.api.packet.payloads.operator; + +import nl.iobyte.themeparkconnector.api.packet.objects.AbstractPacketPayload; +import java.util.UUID; + +public class ClientOperatorClickPayload extends AbstractPacketPayload { + + private UUID uuid; + private String item_id; + + public UUID getUUID() { + return uuid; + } + + public String getItemID() { + return item_id; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/player/PlayerStateService.java b/src/main/java/nl/iobyte/themeparkconnector/api/player/PlayerStateService.java new file mode 100644 index 0000000..91bd52f --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/player/PlayerStateService.java @@ -0,0 +1,61 @@ +package nl.iobyte.themeparkconnector.api.player; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.state.states.IdleState; +import org.bukkit.Bukkit; +import java.util.concurrent.atomic.AtomicLong; + +public class PlayerStateService { + + private int TaskID = 0; + private int strike = 0; + + /** + * Try to start pushing player state data + */ + public void start() { + if(TaskID != 0) + return; + + //Check if shutting down + if(ThemeParkConnector.getInstance().isDisabling()) + return; + + //Start pushing data asynchronously + strike = -1; + AtomicLong stamp = new AtomicLong(System.currentTimeMillis()); + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + TaskID = Bukkit.getScheduler().runTaskTimerAsynchronously(ThemeParkConnector.getInstance(), () -> { + int connected = 0; + for(ClientConnection client : api.getNetworkingService().getClients()) + if(client.isConnected()) + connected++; + + //If no players connected add strike + if(connected != 0) { + strike = 0; + return; + } + + strike++; + if(strike > 2) { + api.getNetworkingService().stop(); + api.getStateService().setState(new IdleState("No users online, closed connection")); + } + }, 0, 10 * 20).getTaskId(); + } + + /** + * Stop pushing player state data + */ + public void stop() { + if(TaskID == 0) + return; + + Bukkit.getScheduler().cancelTask(TaskID); + TaskID = 0; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/show/ShowService.java b/src/main/java/nl/iobyte/themeparkconnector/api/show/ShowService.java new file mode 100644 index 0000000..eb6cdaf --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/show/ShowService.java @@ -0,0 +1,130 @@ +package nl.iobyte.themeparkconnector.api.show; + +import com.google.gson.Gson; +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import nl.iobyte.menuapi.item.ItemBuilder; +import nl.iobyte.themepark.ThemePark; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.show.objects.Ticket; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.*; + +public class ShowService { + + private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + + public String redeemTicket(UUID uuid, String voucher) { + if(ThemeParkConnector.getInstance().getEconomy() == null) + return "No economy manager found"; + + if(uuid == null || voucher == null) + return "Invalid parameters"; + + try { + ArrayList> resultSet = ThemePark.getInstance().getAPI().getDatabaseService().executeQuery( + "remote", + "SELECT `shows`.id, `shows`.title, seats.seat, seats.date, `shows`.vault_price, paid FROM seats INNER JOIN `shows` ON `shows`.id=seats.show_id WHERE uuid=? AND voucher=? AND loaded=0 LIMIT 1", + new HashMap<>(){{ + put(1, uuid.toString().replace("-", "")); + put(2, voucher); + }} + ); + + if(resultSet == null) + return "Database error"; + + if(resultSet.isEmpty()) + return "No results"; + + String id = ""; + String name = ""; + int seat = 0; + Date date = new Date(); + Ticket ticket = new Ticket(voucher, id, seat, date, uuid); + for(Map map : resultSet) { + id = map.get("id").toString(); + double price = Double.parseDouble((String) map.get("vault_price")); + if(!((boolean) map.get("paid"))) { + if (!ThemeParkConnector.getInstance().getEconomy().withdrawPlayer(Bukkit.getOfflinePlayer(uuid), price).transactionSuccess()) { + String finalId = id; + ThemePark.getInstance().getAPI().getDatabaseService().execute( + "remote", + "DELETE FROM seats WHERE uuid=? AND voucher=? AND show_id=?", + new HashMap<>(){{ + put(1, uuid.toString().replace("-", "")); + put(2, voucher); + put(3, finalId); + }} + ); + return "Not enough money"; + } + } + + name = (String) map.get("title"); + seat = (int) map.get("seat"); + date = (Timestamp) map.get("date"); + ticket = new Ticket(voucher, id, seat, date, uuid); + String finalId = id; + ThemePark.getInstance().getAPI().getDatabaseService().execute( + "remote", + "UPDATE seats SET paid=1 WHERE uuid=? AND voucher=? AND show_id=?", + new HashMap<>(){{ + put(1, uuid.toString().replace("-", "")); + put(2, voucher); + put(3, finalId); + }} + ); + } + + ItemBuilder builder = new ItemBuilder(Material.PAPER); + builder.setName("&f" + name); + List list = new ArrayList<>(); + list.add("&6Seat: &f" + ticket.getSeat()); + list.add("&6Date: &f" +format.format(ticket.getShowDate())); + builder.setLore(list); + + Gson g = new Gson(); + NBTItem item = new NBTItem(builder.getItem()); + NBTCompound compound = item.addCompound("data"); + compound.setString("ticket", g.toJson(ticket, Ticket.class)); + + Player player = Bukkit.getPlayer(uuid); + player.getInventory().addItem(item.getItem()); + return "true"; + } catch(Exception e) { + e.printStackTrace(); + } + + return "Something went wrong"; + } + + public boolean setValid(Ticket ticket) { + if(ticket == null) + return false; + + try { + int i = ThemePark.getInstance().getAPI().getDatabaseService().executeUpdate( + "remote", + "UPDATE seats SET loaded=1 WHERE uuid=? AND voucher=? AND show_id=?", + new HashMap<>(){{ + put(1, ticket.getUUID().toString().replace("-", "")); + put(2, ticket.getID()); + put(3, ticket.getShowID()); + }} + ); + + if(i > 0) + return true; + } catch(Exception e) { + e.printStackTrace(); + } + + return false; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/show/objects/Ticket.java b/src/main/java/nl/iobyte/themeparkconnector/api/show/objects/Ticket.java new file mode 100644 index 0000000..6f78265 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/show/objects/Ticket.java @@ -0,0 +1,41 @@ +package nl.iobyte.themeparkconnector.api.show.objects; + +import java.util.Date; +import java.util.UUID; + +public class Ticket { + + private final String show, id; + private final int seat; + private final Date date; + private final UUID uuid; + + public Ticket(String id, String show, int seat, Date date, UUID uuid) { + this.id = id; + this.show = show; + this.seat = seat; + this.date = date; + this.uuid = uuid; + } + + public String getID() { + return id; + } + + public String getShowID() { + return show; + } + + public int getSeat() { + return seat; + } + + public Date getShowDate() { + return date; + } + + public UUID getUUID() { + return uuid; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/state/StateService.java b/src/main/java/nl/iobyte/themeparkconnector/api/state/StateService.java new file mode 100644 index 0000000..e933b5e --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/state/StateService.java @@ -0,0 +1,51 @@ +package nl.iobyte.themeparkconnector.api.state; + +import nl.iobyte.themeparkconnector.api.state.objects.AbstractState; +import nl.iobyte.themeparkconnector.api.state.states.ConnectedState; +import nl.iobyte.themeparkconnector.api.state.states.ConnectingState; +import nl.iobyte.themeparkconnector.api.state.states.IdleState; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; + +public class StateService { + + private AbstractState currentState = new IdleState(); + private final List> listeners = new ArrayList<>(); + + /** + * Register listener for State changes + * @param listener BiConsumer + */ + public void addListener(BiConsumer listener) { + listeners.add(listener); + } + + /** + * Get current state + * @return AbstractState + */ + public AbstractState getCurrentState() { + return currentState; + } + + public boolean isConnecting() { + return currentState instanceof ConnectingState; + } + + public boolean isConnected() { + return currentState instanceof ConnectedState; + } + + /** + * Set current state + * @param currentState AbstractState + */ + public void setState(AbstractState currentState) { + AbstractState oldState = this.currentState; + this.currentState = currentState; + for(BiConsumer listener : listeners) + listener.accept(oldState, currentState); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/state/objects/AbstractState.java b/src/main/java/nl/iobyte/themeparkconnector/api/state/objects/AbstractState.java new file mode 100644 index 0000000..0e56599 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/state/objects/AbstractState.java @@ -0,0 +1,47 @@ +package nl.iobyte.themeparkconnector.api.state.objects; + +public class AbstractState { + + private final String name, description; + private final boolean canConnect, clientConnect; + + public AbstractState(String name, String description, boolean canConnect, boolean clientConnect) { + this.name = name; + this.description = description; + this.canConnect = canConnect; + this.clientConnect = clientConnect; + } + + /** + * Get state name + * @return String + */ + public String getType() { + return name; + } + + /** + * Get description of state + * @return String + */ + public String getDescription() { + return description; + } + + /** + * Get if server can connect + * @return boolean + */ + public boolean canConnect() { + return canConnect; + } + + /** + * Get if client can connect + * @return boolean + */ + public boolean canClientConnect() { + return clientConnect; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/state/states/ConnectedState.java b/src/main/java/nl/iobyte/themeparkconnector/api/state/states/ConnectedState.java new file mode 100644 index 0000000..a1e9571 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/state/states/ConnectedState.java @@ -0,0 +1,11 @@ +package nl.iobyte.themeparkconnector.api.state.states; + +import nl.iobyte.themeparkconnector.api.state.objects.AbstractState; + +public class ConnectedState extends AbstractState { + + public ConnectedState() { + super("Connected", "Connected to ThemeParkConnector network", false, true); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/state/states/ConnectingState.java b/src/main/java/nl/iobyte/themeparkconnector/api/state/states/ConnectingState.java new file mode 100644 index 0000000..1525a4b --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/state/states/ConnectingState.java @@ -0,0 +1,11 @@ +package nl.iobyte.themeparkconnector.api.state.states; + +import nl.iobyte.themeparkconnector.api.state.objects.AbstractState; + +public class ConnectingState extends AbstractState { + + public ConnectingState() { + super("Connecting", "Attempting to connect to ThemeParkConnector network", false, false); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/api/state/states/IdleState.java b/src/main/java/nl/iobyte/themeparkconnector/api/state/states/IdleState.java new file mode 100644 index 0000000..d399396 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/api/state/states/IdleState.java @@ -0,0 +1,15 @@ +package nl.iobyte.themeparkconnector.api.state.states; + +import nl.iobyte.themeparkconnector.api.state.objects.AbstractState; + +public class IdleState extends AbstractState { + + public IdleState() { + this("Nothing going on"); + } + + public IdleState(String description) { + super("Idle", description, true, false); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/ThemeParkConnectorCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/ThemeParkConnectorCommand.java new file mode 100644 index 0000000..6c826fb --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/ThemeParkConnectorCommand.java @@ -0,0 +1,32 @@ +package nl.iobyte.themeparkconnector.commands; + +import nl.iobyte.commandapi.CommandFactory; +import nl.iobyte.commandapi.middlewares.PermissionMiddleware; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.commands.subcommands.*; +import nl.iobyte.themeparkconnector.commands.subcommands.operator.OperatorCommands; + +public class ThemeParkConnectorCommand { + + //Load command data + public ThemeParkConnectorCommand() { + CommandFactory factory = new CommandFactory("themeparkconnector"); + factory.addSubCommand(new HelpCommand(factory)) + .addSubCommand(new ConnectCommand()); + + //Admin + factory.addSubCommand(new StatusCommand()) + .addSubCommand(new NotificationCommand()) + .addSubCommand(new DisconnectCommand()); + + //Operator + OperatorCommands.load(factory); + + //Middleware + factory.addMiddleware(new PermissionMiddleware()); + + //Register command + factory.registerCommand(ThemeParkConnector.getInstance()); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/arguments/OperatorArgument.java b/src/main/java/nl/iobyte/themeparkconnector/commands/arguments/OperatorArgument.java new file mode 100644 index 0000000..226d6f6 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/arguments/OperatorArgument.java @@ -0,0 +1,67 @@ +package nl.iobyte.themeparkconnector.commands.arguments; + +import nl.iobyte.commandapi.interfaces.ICommandArgument; +import nl.iobyte.commandapi.objects.ArgumentCheck; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItem; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItemState; +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import java.util.Arrays; +import java.util.List; + +public class OperatorArgument implements ICommandArgument { + + /** + * Message to display when giving an error + * @return String + */ + public String getMessage(String[] args) { + return "Incorrect operator information: "+ ChatColor.WHITE + + String.join( + ",", + Arrays.asList(args).subList(0, Math.min(args.length, 3)) + ); + } + + /** + * Check if argument is valid OperatorItemState + * @param sender CommandSender + * @param args Arguments passed by Command + * @param previousArguments Previous arguments + * @return Boolean + */ + public ArgumentCheck checkArgument(CommandSender sender, String[] args, List previousArguments) { + if(args.length < 3) + return new ArgumentCheck(false, 1); + + AttractionOperator operator = ThemeParkConnector.getInstance().getAPI().getOperatorService().getOperator(args[0]); + if(operator == null) + return new ArgumentCheck(false, 1); + + OperatorItem item = operator.getPanel().getItem(args[1]); + if(item == null) + return new ArgumentCheck(false, 1); + + return new ArgumentCheck(item.hasState(args[2]), 3); + } + + /** + * Get OperatorItemState passed by command + * @param sender CommandSender + * @param args Arguments passed by Command + * @param previousArguments Previous arguments + * @return OperatorItemState + */ + public OperatorItemState getArgument(CommandSender sender, String[] args, List previousArguments) { + AttractionOperator operator = ThemeParkConnector.getInstance().getAPI().getOperatorService().getOperator(args[0]); + previousArguments.add(operator); + + OperatorItem item = operator.getPanel().getItem(args[1]); + previousArguments.add(item); + + return item.getState(args[2]); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/ConnectCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/ConnectCommand.java new file mode 100644 index 0000000..a5ede0d --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/ConnectCommand.java @@ -0,0 +1,46 @@ +package nl.iobyte.themeparkconnector.commands.subcommands; + +import nl.iobyte.commandapi.interfaces.SubCommand; +import nl.iobyte.themepark.api.attraction.objects.Attraction; +import nl.iobyte.themepark.commands.arguments.AttractionArgument; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.message.Text; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import java.util.List; + +public class ConnectCommand extends SubCommand { + + public ConnectCommand() { + super(new String[]{"connect"}); + + addSyntax("/themeparkconnector connect ") + .addArgument(new AttractionArgument()) + .setAllowConsole(false); + } + + //Attempt to generate an url for the Player + public void onPlayerCommand(Player player, List list, int i) { + Attraction attraction = (Attraction) list.get(0); + AttractionOperator operator = ThemeParkConnector.getInstance().getAPI().getOperatorService().getOperator(attraction.getID()); + if(operator == null) { + player.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &4There is no operator for this attraction")); + return; + } + + ClientConnection client = ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClient( + player.getUniqueId() + ); + if(!operator.canClientConnect(client)) { + player.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &4You can't connect to the operator of this attraction")); + return; + } + + client.publishURL(attraction.getID()); + } + + public void onConsoleCommand(CommandSender commandSender, List list, int i) { } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/DisconnectCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/DisconnectCommand.java new file mode 100644 index 0000000..23e5d35 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/DisconnectCommand.java @@ -0,0 +1,38 @@ +package nl.iobyte.themeparkconnector.commands.subcommands; + +import nl.iobyte.commandapi.interfaces.SubCommand; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themeparkconnector.api.message.Text; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import java.util.List; + +public class DisconnectCommand extends SubCommand { + + public DisconnectCommand() { + super("themeparkconnector.admin", "disconnect"); + + addSyntax("/themeparkconnector disconnect"); + } + + //Attempt to generate an url for the Player + public void onPlayerCommand(Player player, List list, int i) { + onConsoleCommand(player, list, i); + } + + public void onConsoleCommand(CommandSender sender, List list, int i) { + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + try { + if (api.getStateService().isConnected()) + api.getNetworkingService().stop(); + } catch (Exception e) { + e.printStackTrace(); + sender.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &4Something went wrong while closing connection")); + return; + } + + sender.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &aConnection has been closed")); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/HelpCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/HelpCommand.java new file mode 100644 index 0000000..72b5922 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/HelpCommand.java @@ -0,0 +1,71 @@ +package nl.iobyte.themeparkconnector.commands.subcommands; + +import nl.iobyte.commandapi.CommandFactory; +import nl.iobyte.commandapi.arguments.number.IntegerArgument; +import nl.iobyte.commandapi.interfaces.SubCommand; +import nl.iobyte.themeparkconnector.api.message.Text; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import java.util.List; + +public class HelpCommand extends SubCommand { + + private final CommandFactory factory; + + public HelpCommand(CommandFactory factory) { + super(new String[]{"help"}); + + this.factory = factory; + addSyntax("/themeparkconnector help"); + addSyntax("/themeparkconnector help ") + .addArgument(new IntegerArgument()); + } + + public void onPlayerCommand(Player player, List list, int i) { + onConsoleCommand(player, list, i); + } + + //Send CommandSender list of commands it has access to + public void onConsoleCommand(CommandSender sender, List list, int i) { + List commands = factory.getApplicableSubCommands(sender); + if(commands.size() <= 5) { + sendSinglePage(sender, commands); + return; + } + + sendMultiPage(sender, commands, list, i); + } + + private void sendSinglePage(CommandSender sender, List commands) { + sender.sendMessage(Text.color("&f&l>==== &6&lThemeParkConnector &l&f====<")); + for (SubCommand command : commands) + sender.sendMessage(Text.color("&f" + command.getApplicableSyntaxList(sender).get(0).getUsage())); + + sender.sendMessage(Text.color("&f&l>==== &6&lThemeParkConnector &l&f====<")); + } + + private void sendMultiPage(CommandSender sender, List commands, List list, int i) { + int page = 1; + int pages = (int) Math.ceil(((double) commands.size()) / 5); + if(i == 1) + page = (Integer) list.get(0); + + if(page < 1 || page > pages) { + sender.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ Page &6"+page+" &fdoesn't exist")); + return; + } + + page--; + int start = page * 5; + int end = page * 5 + 5; + if(end > commands.size()) + end = commands.size(); + + sender.sendMessage(Text.color("&f&l>==== &6&lThemeParkConnector &f(&6"+(page + 1)+"&f/&6"+pages+"&f) &l&f====<")); + for(i = start; i < end; i++) + sender.sendMessage(Text.color("&f" + commands.get(i).getApplicableSyntaxList(sender).get(0).getUsage())); + + sender.sendMessage(Text.color("&f&l>==== &6&lThemeParkConnector &l&f====<")); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/NotificationCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/NotificationCommand.java new file mode 100644 index 0000000..c2e3e72 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/NotificationCommand.java @@ -0,0 +1,44 @@ +package nl.iobyte.themeparkconnector.commands.subcommands; + +import nl.iobyte.commandapi.arguments.MessageArgument; +import nl.iobyte.commandapi.arguments.PlayersArgument; +import nl.iobyte.commandapi.interfaces.SubCommand; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.message.Text; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.client.objects.Notification; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import java.util.List; + +public class NotificationCommand extends SubCommand { + + public NotificationCommand() { + super("themeparkconnector.admin", "notification"); + + addSyntax("/themeparkconnector notification <message>") + .addArgument(new PlayersArgument()) + .addArgument(new MessageArgument()) + .addArgument(new MessageArgument()); + } + + public void onPlayerCommand(Player player, List<Object> objects, int i) { + onConsoleCommand(player, objects, i); + } + + //Get Notification data and send to Player(s) + public void onConsoleCommand(CommandSender sender, List<Object> objects, int i) { + Notification notification = new Notification((String) objects.get(1), (String) objects.get(2)); + for(Player player : (List<Player>) objects.get(0)) { + ClientConnection client = ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClient(player.getUniqueId()); + if(!client.isConnected()) + continue; + + i++; + notification.send(client); + } + + sender.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ The notification has been send to &6"+i+" &fclients")); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/StatusCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/StatusCommand.java new file mode 100644 index 0000000..94f75b0 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/StatusCommand.java @@ -0,0 +1,49 @@ +package nl.iobyte.themeparkconnector.commands.subcommands; + +import nl.iobyte.commandapi.interfaces.SubCommand; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.state.objects.AbstractState; +import nl.iobyte.themeparkconnector.api.message.Text; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import java.util.List; + +public class StatusCommand extends SubCommand { + + public StatusCommand() { + super("themeparkconnector.admin", "status"); + + addSyntax("/themeparkconnector status"); + } + + public void onPlayerCommand(Player player, List<Object> objects, int i) { + onConsoleCommand(player, objects, i); + } + + //Give CommandSender overview of Plugin status + public void onConsoleCommand(CommandSender sender, List<Object> objects, int i) { + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + AbstractState state = api.getStateService().getCurrentState(); + + sender.sendMessage(Text.color("&f&l>==== &6&lThemeParkConnector &l&f====<")); + sender.sendMessage(Text.color("&6State: &f"+state.getType())); + sender.sendMessage(Text.color("&6Clients: &f"+getClientCount()+"&6/&f"+ Bukkit.getOnlinePlayers().size())); + sender.sendMessage(Text.color("&6Description:")); + sender.sendMessage(Text.color("&f"+state.getDescription())); + sender.sendMessage(Text.color("&f&l>==== &6&lThemeParkConnector &l&f====<")); + } + + //Get clients connected to ThemeParkConnector + private String getClientCount() { + int i = 0; + for(ClientConnection client : ThemeParkConnector.getInstance().getAPI().getNetworkingService().getClients()) + if(client.isConnected()) + i++; + + return Integer.toString(i); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorCommands.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorCommands.java new file mode 100644 index 0000000..e26ad82 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorCommands.java @@ -0,0 +1,12 @@ +package nl.iobyte.themeparkconnector.commands.subcommands.operator; + +import nl.iobyte.commandapi.CommandFactory; + +public class OperatorCommands { + + public static void load(CommandFactory factory) { + factory.addSubCommand(new OperatorReloadCommand()) + .addSubCommand(new OperatorStateCommand()); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorReloadCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorReloadCommand.java new file mode 100644 index 0000000..9adbb9e --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorReloadCommand.java @@ -0,0 +1,29 @@ +package nl.iobyte.themeparkconnector.commands.subcommands.operator; + +import nl.iobyte.commandapi.interfaces.SubCommand; +import nl.iobyte.themeparkconnector.api.load.objects.OperatorDataLoader; +import nl.iobyte.themeparkconnector.api.message.Text; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import java.util.List; + +public class OperatorReloadCommand extends SubCommand { + + public OperatorReloadCommand() { + super("themeparkconnector.admin", "operator", "reload"); + + addSyntax("/themeparkconnector operator reload"); + } + + @Override + public void onPlayerCommand(Player player, List<Object> list, int i) { + onConsoleCommand(player, list, i); + } + + @Override + public void onConsoleCommand(CommandSender sender, List<Object> list, int i) { + sender.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &6Attempting to load ride operators")); + new OperatorDataLoader().load(); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorStateCommand.java b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorStateCommand.java new file mode 100644 index 0000000..f2d722f --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/commands/subcommands/operator/OperatorStateCommand.java @@ -0,0 +1,60 @@ +package nl.iobyte.themeparkconnector.commands.subcommands.operator; + +import nl.iobyte.commandapi.arguments.StringArgument; +import nl.iobyte.commandapi.interfaces.SubCommand; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.event.operator.OperatorStateEvent; +import nl.iobyte.themeparkconnector.api.message.Text; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItem; +import nl.iobyte.themeparkconnector.api.operator.objects.panel.OperatorItemState; +import nl.iobyte.themeparkconnector.commands.arguments.OperatorArgument; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import java.util.List; + +public class OperatorStateCommand extends SubCommand { + + public OperatorStateCommand() { + super("themeparkconnector.admin", "operator", "state"); + + addSyntax("/themeparkconnector operator state <attraction> <item> <state>") + .addArgument(new OperatorArgument()); + + addSyntax("/themeparkconnector operator state <attraction> <item> <state> <data>") + .addArgument(new OperatorArgument()) + .addArgument(new StringArgument()); + } + + @Override + public void onPlayerCommand(Player player, List<Object> list, int i) { + onConsoleCommand(player, list, i); + } + + @Override + public void onConsoleCommand(CommandSender sender, List<Object> list, int i) { + AttractionOperator operator = (AttractionOperator) list.get(0); + OperatorItem item = (OperatorItem) list.get(1); + OperatorItemState state = (OperatorItemState) list.get(2); + OperatorItemState old = item.getActiveState(); + if(state.getID().equals(old.getID())) { + if(sender instanceof Player) + sender.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &4Item already in state: &f"+state.getID())); + + return; + } + + item.setActiveState(state.getID()); + ThemeParkConnector.getInstance().getAPI().getEventDispatcher().call(new OperatorStateEvent( + operator, + item, + old, + state, + i == 0 ? "" : (String) list.get(3) + )); + + if(sender instanceof Player) + sender.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &aSuccessfully changed state of item to: &f"+state.getID())); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/listeners/AttractionListener.java b/src/main/java/nl/iobyte/themeparkconnector/listeners/AttractionListener.java new file mode 100644 index 0000000..3818cf2 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/listeners/AttractionListener.java @@ -0,0 +1,32 @@ +package nl.iobyte.themeparkconnector.listeners; + +import nl.iobyte.themepark.api.event.attraction.AttractionStatusChangeEvent; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.client.ClientConnection; +import nl.iobyte.themeparkconnector.api.network.packet.operator.StatusChangePacket; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class AttractionListener implements Listener { + + @EventHandler + public void onChange(AttractionStatusChangeEvent e) { + AttractionOperator operator = ThemeParkConnector.getInstance().getAPI().getOperatorService().getOperator(e.getAttraction().getID()); + if(operator == null) + return; + + ClientConnection client = operator.getClientConnection(); + if(client == null) + return; + + //Packet + ThemeParkConnector.getInstance().getAPI().getNetworkingService().send( + client, + new StatusChangePacket( + e.getCurrent() + ) + ); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/listeners/OperatorListener.java b/src/main/java/nl/iobyte/themeparkconnector/listeners/OperatorListener.java new file mode 100644 index 0000000..31788dd --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/listeners/OperatorListener.java @@ -0,0 +1,45 @@ +package nl.iobyte.themeparkconnector.listeners; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.event.operator.OperatorConnectEvent; +import nl.iobyte.themeparkconnector.api.event.operator.OperatorDisconnectEvent; +import nl.iobyte.themeparkconnector.api.event.operator.OperatorStateEvent; +import nl.iobyte.themeparkconnector.api.network.packet.operator.OperatorStatePacket; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class OperatorListener implements Listener { + + @EventHandler + public void onConnect(OperatorConnectEvent e) { + String command = e.getOperator().getStartCommand(); + if(command == null || command.isEmpty()) + return; + + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command); + } + + @EventHandler + public void onState(OperatorStateEvent e) { + //Change Packet + ThemeParkConnector.getInstance().getAPI().getNetworkingService().send( + e.getOperator().getClientConnection(), + new OperatorStatePacket( + e.getItem().getID(), + e.getCurrent(), + e.getData() + ) + ); + } + + @EventHandler + public void onDisconnect(OperatorDisconnectEvent e) { + String command = e.getOperator().getStopCommand(); + if(command == null || command.isEmpty()) + return; + + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/listeners/PlayerListener.java b/src/main/java/nl/iobyte/themeparkconnector/listeners/PlayerListener.java new file mode 100644 index 0000000..b26ead3 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/listeners/PlayerListener.java @@ -0,0 +1,31 @@ +package nl.iobyte.themeparkconnector.listeners; + +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.ThemeParkConnectorAPI; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import java.util.UUID; + +public class PlayerListener implements Listener { + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + Player player = e.getPlayer(); + ThemeParkConnectorAPI api = ThemeParkConnector.getInstance().getAPI(); + + //Register ClientConnection + api.getNetworkingService().register(player); + } + @EventHandler + public void onQuit(PlayerQuitEvent e) { + Player player = e.getPlayer(); + UUID uuid = player.getUniqueId(); + + //Remove ClientConnection + ThemeParkConnector.getInstance().getAPI().getNetworkingService().remove(uuid); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/listeners/RideCountListener.java b/src/main/java/nl/iobyte/themeparkconnector/listeners/RideCountListener.java new file mode 100644 index 0000000..36b97de --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/listeners/RideCountListener.java @@ -0,0 +1,31 @@ +package nl.iobyte.themeparkconnector.listeners; + +import nl.iobyte.themepark.ThemePark; +import nl.iobyte.themepark.api.event.ridecount.RideCountAddEvent; +import nl.iobyte.themepark.api.ridecount.objects.RideCount; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import java.util.HashMap; + +public class RideCountListener implements Listener { + + @EventHandler + public void onAdd(RideCountAddEvent e) { + RideCount rc = e.getRideCount(); + ThemePark.getInstance().getAPI().getDatabaseService().executeAsync( + "remote", + "INSERT INTO ridecounts(uuid, attraction_id, year, month, week, day, count) VALUES (?,?,?,?,?,?,?) ON DUPLICATE KEY UPDATE count=?", + new HashMap<>() {{ + put(1, rc.getUUID().toString()); + put(2, rc.getAttractionID()); + put(3, rc.getYear()); + put(4, rc.getMonth()); + put(5, rc.getWeek()); + put(6, rc.getDay()); + put(7, rc.getCount()); + put(8, rc.getCount()); + }} + ); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/listeners/RideOperatorListener.java b/src/main/java/nl/iobyte/themeparkconnector/listeners/RideOperatorListener.java new file mode 100644 index 0000000..7a91eeb --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/listeners/RideOperatorListener.java @@ -0,0 +1,79 @@ +package nl.iobyte.themeparkconnector.listeners; + +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.TextComponent; +import nl.iobyte.themepark.ThemePark; +import nl.iobyte.themepark.api.attraction.objects.Attraction; +import nl.iobyte.themepark.api.message.MessageKey; +import nl.iobyte.themepark.utils.LocationUtil; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.config.enums.StorageKey; +import nl.iobyte.themeparkconnector.api.message.Text; +import nl.iobyte.themeparkconnector.api.operator.objects.AttractionOperator; +import org.bukkit.Bukkit; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +public class RideOperatorListener implements Listener { + + @EventHandler + public void onPlace(SignChangeEvent e) { + if(e.getLines().length < 2) + return; + + if(!e.getLine(0).equalsIgnoreCase(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SIGN_CONTROL_NAME))) + return; + + String id = e.getLine(1); + if(!ThemePark.getInstance().getAPI().getAttractionService().hasAttraction(id)) { + e.getPlayer().sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &4There is no attraction with ID: &f"+id)); + return; + } + + if(!ThemeParkConnector.getInstance().getAPI().getOperatorService().hasOperator(id)) { + e.getPlayer().sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &4There is no operator for this attraction")); + return; + } + + e.setLine(0, Text.color(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SIGN_CONTROL_TITLE))); + e.getPlayer().sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &aSuccessfully made an operator sign")); + } + + @EventHandler + public void onClick(PlayerInteractEvent e) { + if(e.getAction() == Action.LEFT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_AIR) + return; + + if(e.getClickedBlock() == null) + return; + + Block block = e.getClickedBlock(); + if(!(block.getState() instanceof Sign)) + return; + + Sign sign = (Sign) block.getState(); + if(!ChatColor.stripColor(sign.getLine(0)).equals(Text.color(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SIGN_CONTROL_TITLE)))) + return; + + String id = ChatColor.stripColor(sign.getLine(1)); + if(id == null || id.isEmpty()) + return; + + Player player = e.getPlayer(); + if(!ThemePark.getInstance().getAPI().getAttractionService().hasAttraction(id)) { + player.sendMessage(Text.color("&6&lThemeParkConnectorMC &f➢ &4No attraction with ID: &f"+id)); + return; + } + + Bukkit.dispatchCommand(player, "/tpc connect "+id); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/listeners/TicketListener.java b/src/main/java/nl/iobyte/themeparkconnector/listeners/TicketListener.java new file mode 100644 index 0000000..15b7f74 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/listeners/TicketListener.java @@ -0,0 +1,180 @@ +package nl.iobyte.themeparkconnector.listeners; + +import com.google.gson.Gson; +import de.tr7zw.changeme.nbtapi.NBTCompound; +import de.tr7zw.changeme.nbtapi.NBTItem; +import nl.iobyte.themepark.utils.LocationUtil; +import nl.iobyte.themeparkconnector.ThemeParkConnector; +import nl.iobyte.themeparkconnector.api.config.enums.StorageKey; +import nl.iobyte.themeparkconnector.api.event.ticket.TicketScanEvent; +import nl.iobyte.themeparkconnector.api.message.Text; +import nl.iobyte.themeparkconnector.api.show.objects.Ticket; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class TicketListener implements Listener { + + private final SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy"); + + @EventHandler + public void onPlace(SignChangeEvent e) { + if(e.getLines().length < 4) + return; + + if(!e.getLine(0).equalsIgnoreCase(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SIGN_SCAN_NAME))) + return; + + Player player = e.getPlayer(); + if(e.getLine(1) == null || e.getLine(1).isEmpty()) { + player.sendMessage(Text.color("&4Can't make a TicketScanner with empty show ID.")); + return; + } + + if(e.getLine(2) == null || e.getLine(2).isEmpty()) { + player.sendMessage(Text.color("&4Can't make a TicketScanner with empty show Date.")); + return; + } + + String date = e.getLine(2); + if(isValidFormat("HH:mm", date) && isValidFormat("HH:mm dd-MM-yyyy", date)) { + player.sendMessage(Text.color("&4Can't make a TicketScanner with incorrect date, example: 20:00 or 20:00 04-04-2019")); + return; + } + + if(e.getLine(3) == null || e.getLine(3).isEmpty()) { + player.sendMessage(Text.color("&4Can't make a TicketScanner with empty teleport location.")); + return; + } + + String world = player.getLocation().getWorld().getName(); + if(LocationUtil.fromString(world + ":" + e.getLine(3)) == null) { + player.sendMessage(Text.color("&4Can't make a TicketScanner with incorrect teleport location.")); + return; + } + + e.setLine(0, Text.color(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SIGN_SCAN_TITLE))); + player.sendMessage(Text.color("&aSuccessfully made a TicketScanner")); + } + + @EventHandler + public void onScan(PlayerInteractEvent e) { + if(e.getAction() == Action.LEFT_CLICK_AIR || e.getAction() == Action.RIGHT_CLICK_AIR) + return; + + if(e.getClickedBlock() == null) + return; + + Block block = e.getClickedBlock(); + if(!(block.getState() instanceof Sign)) + return; + + Sign sign = (Sign) block.getState(); + if(!sign.getLine(0).equals(Text.color(ThemeParkConnector.getInstance().getAPI().getConfigurationManager().getString(StorageKey.SIGN_SCAN_TITLE)))) { + return; + } + + String id = ChatColor.stripColor(sign.getLine(1)); + if(id == null || id.isEmpty()) + return; + + String date = ChatColor.stripColor(sign.getLine(2)); + if(date == null || date.isEmpty()) + return; + + if(isValidFormat("HH:mm", date) && isValidFormat("HH:mm dd-MM-yyyy", date)) { + System.out.println("Invalid date format"); + return; + } + + if(isValidFormat("HH:mm dd-MM-yyyy", date)) + date = date + " " + format.format(new Date()); + + Location location = LocationUtil.fromString(sign.getLocation().getWorld().getName() + ":" + sign.getLine(3)); + if(location == null) { + System.out.println("Invalid Location"); + return; + } + + if(e.getItem() == null) + return; + + ItemStack item = e.getItem(); + if(item.getType() != Material.PAPER) + return; + + NBTItem it = new NBTItem(item); + if(!it.hasKey("data")) { + System.out.println("Missing data key"); + return; + } + + NBTCompound compound = it.getCompound("data"); + if(!compound.hasKey("ticket")) { + System.out.println("Missing ticket key"); + return; + } + + Gson g = new Gson(); + Ticket ticket = g.fromJson(it.getCompound("data").getString("ticket"), Ticket.class); + if(ticket == null) { + System.out.println("Couldn't get ticket class using Gson"); + return; + } + + if(!id.equals(ticket.getShowID())) { + System.out.println("Different show id"); + return; + } + + if(!date.equals(new SimpleDateFormat("HH:mm dd-MM-yyyy").format(ticket.getShowDate()))) { + System.out.println("Different date"); + return; + } + + Player player = e.getPlayer(); + boolean b = ThemeParkConnector.getInstance().getAPI().getShowService().setValid(ticket); + + TicketScanEvent event = new TicketScanEvent(player.getUniqueId(), ticket, b); + Bukkit.getPluginManager().callEvent(event); + + int i = player.getInventory().getHeldItemSlot(); + player.getInventory().remove(player.getInventory().getItem(i)); + if(!b) { + player.sendMessage(Text.color("&4Invalid ticket.")); + return; + } + + if(!event.isCancelled()) + player.teleport(location); + + player.sendMessage(Text.color("&6Used ticket for Show: " + item.getItemMeta().getDisplayName())); + } + + private static boolean isValidFormat(String format, String value) { + try { + SimpleDateFormat sdf = new SimpleDateFormat(format); + Date date = sdf.parse(value); + if(!value.equals(sdf.format(date))) + date = null; + + return date == null; + } catch(ParseException ex) { + return true; + } + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/logger/ThemeParkConnectorLogger.java b/src/main/java/nl/iobyte/themeparkconnector/logger/ThemeParkConnectorLogger.java new file mode 100644 index 0000000..f599268 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/logger/ThemeParkConnectorLogger.java @@ -0,0 +1,13 @@ +package nl.iobyte.themeparkconnector.logger; + +public class ThemeParkConnectorLogger { + + /** + * Print message to the console + * @param msg Message string + */ + public static void toConsole(String msg) { + System.out.println("[ThemeParkConnector] "+msg); + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/sbd/License.java b/src/main/java/nl/iobyte/themeparkconnector/sbd/License.java new file mode 100644 index 0000000..6cd6602 --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/sbd/License.java @@ -0,0 +1,302 @@ +package nl.iobyte.themeparkconnector.sbd; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.plugin.java.JavaPlugin; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * License class for SBDevelopment + * + * v1.6 - Changed on 06-08-2020 + * + * @author Stijn [SBDeveloper] + * @since 23-12-2019 + */ +public class License implements Listener { + /* + This file is part of ThemeParkRidecountAddon. + Copyright (c) 2018-2020 SBDevelopment - All Rights Reserved + Unauthorized copying of this file, via any medium is strictly prohibited + Proprietary and confidential + Written by Stijn Bannink <stijnbannink23@gmail.com>, January 2020 + */ + + private JavaPlugin plugin; // The plugin instance + private String license; // The license code + private String prefix; // The correct prefix for this plugin + private String invalidReason; // The reason the license is invalid, if null it's not invalid! + private static Boolean valid; // If true, it's valid, if false, it's not valid, if null it's not checked! + + /** + * Construct a new license + * @param plugin The Main class [Javaplugin] + * @param prefix The prefix, like TPP or AF + * @param license The license from the config + */ + public License(JavaPlugin plugin, String prefix, String license) { + this.prefix = prefix; + this.plugin = plugin; + this.license = license; + + Bukkit.getPluginManager().registerEvents(this, plugin); + + validateLicense(); + } + + @EventHandler + public void onJoin(PlayerJoinEvent e) { + if (this.invalidReason == null) return; + + Player p = e.getPlayer(); + if (p.isOp() || p.hasPermission("sbd.licensemessages")) { + Bukkit.getScheduler().runTaskLater(this.plugin, () -> p.sendMessage(ChatColor.GOLD + "[" + ChatColor.RED + this.plugin.getName() + ChatColor.GOLD + "] " + ChatColor.RED + "The license is incorrect! Reason: " + ChatColor.GOLD + this.invalidReason), 3 * 20L /* 3 sec */); + } + } + + /** + * Check a license + * + */ + private void validateLicense() { + //STEP 1: Check prefix + if (!this.license.split("-")[0].contains(this.prefix)) { + disable("You used the wrong license for this product."); + return; + } + + //STEP 2: Send license request + String url = "https://sbdplugins.nl/wp-json/lmfwc/v2/licenses/" + this.license; + JsonObject response; + try { + response = sendGETRequestJSON(url); + } catch (IOException e) { + disable("Couldn't send the request."); + return; + } + + if (response == null) { + disable("Couldn't send the request."); + return; + } + + JsonObject dataObject = response.get("data").getAsJsonObject(); + + //STEP 3: Check status + switch(dataObject.get("status").getAsString()) { + case "2": + //Delivered -> Try to activate (double check timesActivated) + break; + case "3": + //Activated! + break; + default: + disable("Your license has a wrong status."); + return; + } + + //STEP 4: Check times activated, and if not activated, activate. + if (dataObject.get("timesActivated").isJsonNull() || dataObject.get("timesActivated").getAsString().equalsIgnoreCase("0")) { + activate(); + return; + } + + //STEP 5: Check expire date + if (dataObject.get("expiresAt").isJsonNull()) { + disable("Your license has no expire date."); + return; + } + + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + Date date; + try { + date = format.parse(dataObject.get("expiresAt").getAsString()); + } catch (ParseException e) { + e.printStackTrace(); + disable("Your license has a wrong expire date."); + return; + } + + if (date == null) { + disable("Your license has a wrong expire date."); + return; + } + + if (!(date.after(new Date()))) { + disable("Your license has expired."); + return; + } + + //STEP 6: Check IP and port. + if (!dataObject.get("ipcheck").getAsBoolean()) { + disable("Your license has been used with another IP. Update it in our Discord."); + return; + } + + if (dataObject.get("port").isJsonNull()) { + disable("Your license has no port."); + return; + } + + String por = dataObject.get("port").getAsString(); + if (!checkPortValue(Bukkit.getServer().getPort(), por)) { + disable("Your license has been used with another Port. Update it in our Discord."); + return; + } + + valid = true; + } + + /** + * Activate the license + * + */ + private void activate() { + //STEP 1: Send license activate request + String url = "https://sbdplugins.nl/wp-json/lmfwc/v2/licenses/activate/" + this.license + "?port=" + Bukkit.getServer().getPort(); + + JsonObject response; + try { + response = sendGETRequestJSON(url); + } catch (IOException e) { + disable("Couldn't send the activate request."); + return; + } + + if (response == null) { + disable("Couldn't send the activate request."); + return; + } + + JsonObject dataObject = response.get("data").getAsJsonObject(); + + //STEP 2: Check status + switch(dataObject.get("status").getAsString()) { + case "2": + //Delivered -> STILL NOT ACTIVATED?! -> Double check + break; + case "3": + //Activated! + break; + default: + disable("Your license has a wrong status."); + return; + } + + //STEP 3: Check times activated, and if still not activated, disable. + if (dataObject.get("timesActivated").isJsonNull() || dataObject.get("timesActivated").getAsString().equalsIgnoreCase("0")) { + disable("Couldn't activate the license."); + } + } + + /** + * Disable the plugin (private) + * + * @param reason The disabling reason + */ + private void disable(String reason) { + this.invalidReason = reason; + + Bukkit.getScheduler().runTask(this.plugin, () -> { + valid = false; + + Bukkit.getLogger().severe("[" + this.plugin.getName() + "] Stopping plugin because licensing system check failed."); + Bukkit.getLogger().severe("[" + this.plugin.getName() + "] Reason: " + reason); + Bukkit.getLogger().severe("[" + this.plugin.getName() + "] Contact the developer if you believe something is wrong on their side."); + Bukkit.getPluginManager().disablePlugin(this.plugin); + }); + } + + /** + * Send an GET request with JSONObject response + * + * @param uri The URL + * + * @return The JSONObject + * @throws IOException URL errors + */ + private JsonObject sendGETRequestJSON(String uri) throws IOException { + URL url = new URL(uri); + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setRequestMethod("GET"); + con.setRequestProperty("User-Agent", "Mozilla/5.0"); + String authStringEnc = "Y2tfMGEzNWEzMWE2NzExNmM3NDg2MGEwYTJhNjUxNGVjZjM4NTBmM2JmMDpjc185NmYxZGNlYjI4MWRkZDExOTBjMzQ3ZjJjYzMwMGNjZTIzYWNhODI1"; + con.setRequestProperty("Authorization", "Basic " + authStringEnc); + int code = con.getResponseCode(); + if (code == 404) { + disable("404_error"); + return null; + } + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuilder response = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + JsonParser parser = new JsonParser(); + return parser.parse(response.toString()).getAsJsonObject(); + } + + private boolean checkPortValue(int input, String dataValue) { + //STEP 1: Check wildcard + if (dataValue.equals("*")) return true; + + //STEP 2: Check if equals + try { + int dataVal = Integer.parseInt(dataValue); + + return input == dataVal; + } catch (NumberFormatException ignored) {} + + //STEP 3: Check if range + if (dataValue.contains("-")) { + String[] dataSplit = dataValue.split("-"); + + //STEP 3.1: Check if min or max is wildcard + if (dataSplit[0].equals("*") && !dataSplit[1].equals("*")) { + int max = Integer.parseInt(dataSplit[1]); + return input <= max; + } else if (dataSplit[1].equals("*") && !dataSplit[0].equals("*")) { + int min = Integer.parseInt(dataSplit[0]); + return min <= input; + } else { + try { + int min = Integer.parseInt(dataSplit[0]); + int max = Integer.parseInt(dataSplit[1]); + + return (min <= input) && (input <= max); + } catch (NumberFormatException ex) { + return false; + } + } + } + + //Else, invalid value + return false; + } + + /** + * Check if the license is valid + * + * @return true -> VALID, false -> INVALID, null -> UNCHECKED + */ + public static boolean isValid() { + return valid; + } + +} diff --git a/src/main/java/nl/iobyte/themeparkconnector/sbd/UpdateManager.java b/src/main/java/nl/iobyte/themeparkconnector/sbd/UpdateManager.java new file mode 100644 index 0000000..130f16d --- /dev/null +++ b/src/main/java/nl/iobyte/themeparkconnector/sbd/UpdateManager.java @@ -0,0 +1,120 @@ +package nl.iobyte.themeparkconnector.sbd; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.function.BiConsumer; + +/** + * Update class for SBDevelopment + * @author Stijn [SBDeveloper] + * @since 18-01-2020 + * @version 1.2 + * + * © Stijn Bannink <stijnbannink23@gmail.com> - All rights reserved. + */ +public class UpdateManager { + + private static String SPIGOT_API = "http://api.spiget.org/v2/resources/%d/versions?size=1&sort=-releaseDate"; + private static String SBDPLUGINS_API = "http://updates.sbdplugins.nl:4000/api/resources/%d"; + + private Plugin plugin; + private String currentVersion; + private int resourceID; + private CheckType type; + private BiConsumer<VersionResponse, String> versionResponse; + + /** + * Construct a new UpdateManager + * + * @param plugin The javaplugin (Main class) + * @param resourceID The resourceID on spigot/sbdplugins + * @param type The check type + */ + public UpdateManager(Plugin plugin, int resourceID, CheckType type) { + this.plugin = plugin; + this.currentVersion = plugin.getDescription().getVersion(); + this.resourceID = resourceID; + this.type = type; + } + + /** + * Handle the response given by check(); + * @param versionResponse The response + * @return The updatemanager + */ + public UpdateManager handleResponse(BiConsumer<VersionResponse, String> versionResponse) { + this.versionResponse = versionResponse; + return this; + } + + /** + * Check for a new version + */ + public void check() { + Bukkit.getScheduler().runTaskAsynchronously(this.plugin, () -> { + try { + HttpURLConnection con = null; + if (type == CheckType.SPIGOT) { + con = (HttpURLConnection) new URL(String.format(SPIGOT_API, this.resourceID)).openConnection(); + } else if (type == CheckType.SBDPLUGINS) { + con = (HttpURLConnection) new URL(String.format(SBDPLUGINS_API, this.resourceID)).openConnection(); + } + + if (con == null) return; + + con.setRequestMethod("GET"); + con.setRequestProperty("User-Agent", "Mozilla/5.0"); + + String version = null; + + BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); + String inputLine; + StringBuilder response = new StringBuilder(); + while ((inputLine = in.readLine()) != null) { + response.append(inputLine); + } + in.close(); + + JsonParser parser = new JsonParser(); + + if (type == CheckType.SPIGOT) { + JsonArray array = parser.parse(response.toString()).getAsJsonArray(); + + version = array.get(0).getAsJsonObject().get("name").getAsString(); + } else if (type == CheckType.SBDPLUGINS) { + JsonObject object = parser.parse(response.toString()).getAsJsonObject(); + + version = object.get("data").getAsJsonObject().get("version").getAsString(); + } + + if (version == null) return; + + boolean latestVersion = version.equalsIgnoreCase(this.currentVersion); + + String finalVersion = version; + Bukkit.getScheduler().runTask(this.plugin, () -> this.versionResponse.accept(latestVersion ? VersionResponse.LATEST : VersionResponse.FOUND_NEW, latestVersion ? this.currentVersion : finalVersion)); + } catch (IOException | NullPointerException e) { + e.printStackTrace(); + Bukkit.getScheduler().runTask(this.plugin, () -> this.versionResponse.accept(VersionResponse.UNAVAILABLE, null)); + } + }); + } + + public enum CheckType { + SPIGOT, SBDPLUGINS + } + + public enum VersionResponse { + LATEST, FOUND_NEW, UNAVAILABLE + } + +} diff --git a/src/main/resources/message.yml b/src/main/resources/message.yml new file mode 100644 index 0000000..e137e3b --- /dev/null +++ b/src/main/resources/message.yml @@ -0,0 +1,14 @@ +version: 1.0 + +prefix: '&6&lThemeParkConnectorMC &r&f➢' + +client: + connect: + click: "{prefix} &fClick here to connect to the &6&lRideOperator" + hover: "&6Minecraft will prompt you to open the web client when you click this message" + unable: "{prefix} &4Currently unable to generate a session" + generate: "{prefix} &6Attempting to generate session..." + connection: + exists: "{prefix} &4Already connected to RideOperator" + open: "{prefix} &aSuccessfully connected to RideOperator" + closed: "{prefix} &4Connection with RideOperator was closed" \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..9e93e58 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,11 @@ +name: ThemeParkConnector +version: 3.0.0 +author: IOByte +website: 'https://www.iobyte.nl' +main: nl.iobyte.themeparkconnector.ThemeParkConnector +depend: [ThemePark] +softdepend: [Vault,PlaceholderAPI,Train_Carts,Multiverse-Core,MultiWorld] +api-version: 1.13 +commands: + tpc: + aliases: [themeparkconnector] \ No newline at end of file diff --git a/src/main/resources/settings.yml b/src/main/resources/settings.yml new file mode 100644 index 0000000..ca4c29e --- /dev/null +++ b/src/main/resources/settings.yml @@ -0,0 +1,16 @@ +version: 1.0 + +license: '' + +socket: + id: 'themepark' + url: 'websocket.iobyte.nl' + panel: 'panel.example.com/control/%ID%/%TOKEN%' + +sign: + control: + name: '[Operator]' + title: '&f[&6&lOperator&f]' + scan: + name: '[TicketScan]' + title: '&f[&6&lTicketScan&f]' \ No newline at end of file diff --git a/themeparkconnector.iml b/themeparkconnector.iml new file mode 100644 index 0000000..6c99dbb --- /dev/null +++ b/themeparkconnector.iml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_11"> + <output url="file://$MODULE_DIR$/target/classes" /> + <output-test url="file://$MODULE_DIR$/target/test-classes" /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" /> + <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: commons-lang:commons-lang:2.6" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: junit:junit:4.10" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.guava:guava:21.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: com.google.code.gson:gson:2.8.0" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: org.yaml:snakeyaml:1.19" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: net.md-5:bungeecord-chat:1.12-SNAPSHOT" level="project" /> + <orderEntry type="library" name="Maven: io.socket:socket.io-client:1.0.1" level="project" /> + <orderEntry type="library" name="Maven: io.socket:engine.io-client:1.0.1" level="project" /> + <orderEntry type="library" name="Maven: com.squareup.okhttp3:okhttp:3.12.12" level="project" /> + <orderEntry type="library" name="Maven: com.squareup.okio:okio:1.15.0" level="project" /> + <orderEntry type="library" name="Maven: org.json:json:20090211" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: nl.iobyte:themepark:3.0.0" level="project" /> + <orderEntry type="library" name="Maven: de.mkammerer.snowflake-id:snowflake-id:0.0.1" level="project" /> + <orderEntry type="library" name="Maven: de.tr7zw:item-nbt-api:2.8.0" level="project" /> + <orderEntry type="library" name="Maven: de.tr7zw:functional-annotations:0.1-SNAPSHOT" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: net.milkbowl.vault:VaultAPI:1.7" level="project" /> + <orderEntry type="library" scope="PROVIDED" name="Maven: org.bukkit:bukkit:1.13.1-R0.1-SNAPSHOT" level="project" /> + </component> +</module> \ No newline at end of file