001package net.tnemc.plugincore.sponge.impl;
002
003/*
004 * The New Plugin Core
005 * Copyright (C) 2022 - 2024 Daniel "creatorfromhell" Vidmar
006 *
007 * This program is free software: you can redistribute it and/or modify
008 * it under the terms of the GNU Affero General Public License as published by
009 * the Free Software Foundation, either version 3 of the License, or
010 * (at your option) any later version.
011 *
012 * This program is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
015 * GNU Affero General Public License for more details.
016 *
017 * You should have received a copy of the GNU Affero General Public License
018 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
019 */
020
021import net.tnemc.item.AbstractItemStack;
022import net.tnemc.plugincore.core.compatibility.CmdSource;
023import net.tnemc.plugincore.core.compatibility.LogProvider;
024import net.tnemc.plugincore.core.compatibility.PlayerProvider;
025import net.tnemc.plugincore.core.compatibility.ProxyProvider;
026import net.tnemc.plugincore.core.compatibility.ServerConnector;
027import net.tnemc.plugincore.core.compatibility.helper.CraftingRecipe;
028import net.tnemc.plugincore.core.compatibility.scheduler.SchedulerProvider;
029import net.tnemc.plugincore.sponge.SpongePluginCore;
030import net.tnemc.plugincore.sponge.impl.scheduler.SpongeScheduler;
031import net.tnemc.sponge.SpongeItemCalculationsProvider;
032import net.tnemc.sponge.SpongeItemStack;
033import org.jetbrains.annotations.NotNull;
034import org.jetbrains.annotations.Nullable;
035import org.spongepowered.api.Sponge;
036import org.spongepowered.api.entity.living.player.server.ServerPlayer;
037import org.spongepowered.api.world.DefaultWorldKeys;
038import revxrsal.commands.command.CommandActor;
039import revxrsal.commands.sponge.actor.SpongeCommandActor;
040
041import java.io.File;
042import java.io.FileOutputStream;
043import java.io.IOException;
044import java.io.InputStream;
045import java.io.OutputStream;
046import java.net.URL;
047import java.net.URLConnection;
048import java.util.Optional;
049import java.util.UUID;
050
051/**
052 * SpongeServerProvider
053 *
054 * @author creatorfromhell
055 * @since 0.1.2.0
056 */
057public class SpongeServerProvider implements ServerConnector {
058
059  private final SpongeItemCalculationsProvider calc = new SpongeItemCalculationsProvider();
060  private final SpongeProxyProvider proxy = new SpongeProxyProvider();
061
062  private final SpongeScheduler scheduler;
063
064  protected String world = null;
065
066  public SpongeServerProvider() {
067    this.scheduler = new SpongeScheduler();
068  }
069
070  @Override
071  public String name() {
072    return "sponge";
073  }
074
075  /**
076   * Used to replace placeholders from a string.
077   *
078   * @param player The player to use for the placeholder replacement.
079   * @param message The message to replace placeholders in.
080   *
081   * @return The string after placeholders have been replaced.
082   */
083  @Override
084  public String replacePlaceholder(final UUID player, final String message) {
085    return message;
086  }
087
088  /**
089   * The proxy provider to use for this implementation.
090   *
091   * @return The proxy provider to use for this implementation.
092   */
093  @Override
094  public ProxyProvider proxy() {
095    return proxy;
096  }
097
098  /**
099   * Used to convert an {@link CommandActor} to a {@link CmdSource}.
100   *
101   * @param actor The command actor.
102   *
103   * @return The {@link CmdSource} for this actor.
104   */
105  @Override
106  public CmdSource<?> source(@NotNull final CommandActor actor) {
107    return new SpongeCMDSource((SpongeCommandActor)actor);
108  }
109
110  /**
111   * Used to get the amount of online players.
112   *
113   * @return The amount of online players.
114   */
115  @Override
116  public int onlinePlayers() {
117    return Sponge.server().onlinePlayers().size();
118  }
119
120  /**
121   * Attempts to find a {@link PlayerProvider player} based on an {@link UUID identifier}.
122   *
123   * @param identifier The identifier
124   *
125   * @return An Optional containing the located {@link PlayerProvider player}, or an empty
126   * Optional if no player is located.
127   */
128  @Override
129  public Optional<PlayerProvider> findPlayer(@NotNull final UUID identifier) {
130    final Optional<ServerPlayer> player = Sponge.server().player(identifier);
131    return player.map(value->new SpongePlayerProvider(value.user(), SpongePluginCore.instance().getContainer()));
132  }
133
134  /**
135   * This is used to return an instance of an {@link PlayerProvider player} based on the provided
136   * instance's player object.
137   *
138   * @param player The instance of the player.
139   *
140   * @return The initialized {@link PlayerProvider player object}.
141   */
142  @Override
143  public PlayerProvider initializePlayer(@NotNull final Object player) {
144    if(player instanceof final ServerPlayer playerObj) {
145      return new SpongePlayerProvider(playerObj.user(), SpongePluginCore.instance().getContainer());
146    }
147    return null;
148  }
149
150  /**
151   * Used to determine if this player has played on this server before.
152   *
153   * @param identifier The {@link UUID} that is associated with the player.
154   *
155   * @return True if the player has played on the server before, otherwise false.
156   */
157  @Override
158  public boolean playedBefore(final UUID identifier) {
159    final Optional<ServerPlayer> player = Sponge.server().player(identifier);
160    return player.map(ServerPlayer::hasPlayedBefore).orElse(false);
161  }
162
163  /**
164   * Used to determine if a player with the specified username has played
165   * before.
166   *
167   * @param name The username to search for.
168   *
169   * @return True if someone with the specified username has played before,
170   * otherwise false.
171   */
172  @Override
173  public boolean playedBefore(final String name) {
174    final Optional<ServerPlayer> player = Sponge.server().player(name);
175    return player.map(ServerPlayer::hasPlayedBefore).orElse(false);
176  }
177
178  /**
179   * Used to determine if a player with the specified username is online.
180   *
181   * @param name The username to search for.
182   *
183   * @return True if someone with the specified username is online.
184   */
185  @Override
186  public boolean online(final String name) {
187    try {
188
189      final Optional<ServerPlayer> player = Sponge.server().player(UUID.fromString(name));
190      return player.map(ServerPlayer::isOnline).orElse(false);
191    } catch (final Exception e) {
192
193      final Optional<ServerPlayer> player = Sponge.server().player(name);
194      return player.map(ServerPlayer::isOnline).orElse(false);
195    }
196  }
197
198  @Override
199  public Optional<UUID> fromName(final String name) {
200    return Optional.empty();
201  }
202
203  /**
204   * Used to locate a username for a specific name. This could be called from either a primary or
205   * secondary thread, and should not call back to the Mojang API ever.
206   *
207   * @param id The {@link UUID} to use for the search.
208   *
209   * @return An optional containing the name if exists, otherwise false.
210   */
211  @Override
212  public Optional<String> fromID(final UUID id) {
213    return Optional.empty();
214  }
215
216  /**
217   * Returns the name of the default world.
218   *
219   * @return The name of the default world.
220   */
221  @Override
222  public String defaultWorld() {
223    if(world == null) {
224      world = Sponge.server().worldManager().world(DefaultWorldKeys.DEFAULT).get().key().asString();
225    }
226    return world;
227  }
228
229  /**
230   * Used to replace colour codes in a string.
231   * @param string The string to format.
232   * @param strip If true, the color codes are striped from the string.
233   * @return The formatted string.
234   */
235  @Override
236  public String replaceColours(final String string, final boolean strip) {
237    return string;
238  }
239
240  @Override
241  public AbstractItemStack<?> stackBuilder() {
242    return new SpongeItemStack();
243  }
244
245  @Override
246  public void saveResource(String resourcePath, final boolean replace) {
247    if (resourcePath != null && ! resourcePath.isEmpty()) {
248
249      resourcePath = resourcePath.replace('\\', '/');
250
251      final LogProvider logger = SpongePluginCore.log();
252      final InputStream in = this.getResource(resourcePath);
253      if (in == null) {
254
255        throw new IllegalArgumentException("The embedded resource '" + resourcePath + "' cannot be found in the jar.");
256      } else {
257
258        final File outFile = new File(SpongePluginCore.directory(), resourcePath);
259        final int lastIndex = resourcePath.lastIndexOf(47);
260        final File outDir = new File(SpongePluginCore.directory(), resourcePath.substring(0, Math.max(lastIndex, 0)));
261        if (!outDir.exists()) {
262          outDir.mkdirs();
263        }
264
265        try {
266          if (outFile.exists() && !replace) {
267          } else {
268
269            final OutputStream out = new FileOutputStream(outFile);
270            final byte[] buf = new byte[1024];
271
272            int len;
273            while((len = in.read(buf)) > 0) {
274              out.write(buf, 0, len);
275            }
276
277            out.close();
278            in.close();
279          }
280        } catch (final IOException ignore) {
281          logger.error("Could not save " + outFile.getName() + " to " + outFile);
282        }
283
284      }
285    } else {
286      throw new IllegalArgumentException("ResourcePath cannot be null or empty");
287    }
288  }
289
290  /**
291   * Provides this implementation's {@link SchedulerProvider scheduler}.
292   *
293   * @return The scheduler for this implementation.
294   */
295  @Override
296  public SpongeScheduler scheduler() {
297    return scheduler;
298  }
299
300  /**
301   * Used to register a crafting recipe to the server.
302   *
303   * @param key The key for the crafting recipe to be registered.
304   * @param recipe The crafting recipe to register.
305   *
306   * @see CraftingRecipe
307   */
308  @Override
309  public void registerCrafting(@NotNull final String key, @NotNull final CraftingRecipe recipe) {
310    //TODO: Sponge Register crafting
311  }
312
313  @Override
314  public SpongeItemCalculationsProvider calculations() {
315    return calc;
316  }
317
318  @Override
319  public @Nullable InputStream getResource(@NotNull final String filename) {
320    try {
321      final URL url = this.getClass().getClassLoader().getResource(filename);
322      if (url == null) {
323        return null;
324      } else {
325        final URLConnection connection = url.openConnection();
326        connection.setUseCaches(false);
327        return connection.getInputStream();
328      }
329    } catch (final IOException var4) {
330      return null;
331    }
332  }
333}