diff --git a/.run/Sculk.run.xml b/.run/Sculk.run.xml
index 62af14e..3184fb5 100644
--- a/.run/Sculk.run.xml
+++ b/.run/Sculk.run.xml
@@ -7,7 +7,7 @@
-
+
diff --git a/pom.xml b/pom.xml
index d25e1b9..06ca6fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,7 +47,7 @@
UTF-8
3.0.0.Beta5-SNAPSHOT
1.0.0.CR3-SNAPSHOT
- 2.23.1
+ 2.17.1
3.23.0
4.1.101.Final
@@ -97,12 +97,6 @@
central
Maven Central
https://repo1.maven.org/maven2/
-
- true
-
-
- true
-
projectlombok.org
@@ -167,11 +161,6 @@
${log4j2.version}
compile
-
- org.slf4j
- slf4j-simple
- 1.7.21
-
org.apache.logging.log4j
log4j-core
@@ -293,27 +282,58 @@
org.apache.maven.plugins
- maven-assembly-plugin
- 3.4.2
-
-
-
- org.sculk.Sculk
-
-
-
- jar-with-dependencies
-
-
+ maven-shade-plugin
+ 3.5.0
+
+
+ com.github.edwgiz
+ maven-shade-plugin.log4j2-cachefile-transformer
+ 2.8.1
+
+
- make-assembly
package
- single
+ shade
+
+
+
+ org.bstats
+
+ org.sculk
+
+
+ false
+
+
+
+
+ org.sculk.Sculk
+
+ true
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+ 21
+ false
+ none
+ ${java.home}/bin/javadoc
+
diff --git a/src/main/java/org/sculk/Server.java b/src/main/java/org/sculk/Server.java
index b94f5a0..4244959 100644
--- a/src/main/java/org/sculk/Server.java
+++ b/src/main/java/org/sculk/Server.java
@@ -9,8 +9,11 @@
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import org.apache.logging.log4j.Logger;
+import org.cloudburstmc.nbt.NbtMap;
+import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
-import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket;
+import org.sculk.api.player.GameMode;
+import org.sculk.api.server.Operators;
import org.sculk.command.CommandSender;
import org.sculk.command.SimpleCommandMap;
import org.sculk.config.Config;
@@ -20,6 +23,7 @@
import org.sculk.event.EventManager;
import org.sculk.event.command.CommandEvent;
import org.sculk.event.player.PlayerCreationEvent;
+import org.sculk.event.player.PlayerLoginEvent;
import org.sculk.lang.Language;
import org.sculk.lang.LanguageKeys;
import org.sculk.lang.LocalManager;
@@ -30,12 +34,13 @@
import org.sculk.network.session.SculkServerSession;
import org.sculk.player.Player;
import org.sculk.player.client.ClientChainData;
-import org.sculk.player.skin.Skin;
+import org.sculk.player.text.RawTextBuilder;
+import org.sculk.player.text.TranslaterBuilder;
import org.sculk.plugin.PluginManager;
+import org.sculk.resourcepack.ResourcePackManager;
import org.sculk.scheduler.Scheduler;
import org.sculk.server.SculkOperators;
import org.sculk.server.SculkWhitelist;
-import org.sculk.utils.SkinUtils;
import org.sculk.utils.TextFormat;
import java.lang.reflect.Constructor;
@@ -67,6 +72,8 @@ public class Server {
private final TerminalConsole console;
private final EventManager eventManager;
private final PluginManager pluginManager;
+ @Getter
+ private final ResourcePackManager resourcePackManager;
private final Injector injector;
private final Scheduler scheduler;
@@ -91,11 +98,15 @@ public class Server {
private final Map playerList = new HashMap<>();
private final Map players = new HashMap<>();
+ @Getter
private String motd;
private String submotd;
+ @Getter
private int maxPlayers;
- private String defaultGamemode;
+ @Getter
private UUID serverId;
+ @Getter
+ private final boolean query;
private long nextTick;
private int tickCounter;
@@ -114,7 +125,8 @@ public Server(LocalManager localManager, Logger logger, String dataPath) {
//Language manager
this.localManager = localManager;
this.language = localManager.getLanguage(this.properties.get(ServerPropertiesKeys.LANGUAGE, DEFAULT_LANGUAGE));
- this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_SELECTED_LANGUAGE, List.of(this.language.getName())));
+ System.out.println(this.properties.get(ServerPropertiesKeys.LANGUAGE, DEFAULT_LANGUAGE));
+ this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_SELECTED_LANGUAGE, List.of(TextFormat.DARK_AQUA + this.language.getName() + TextFormat.RESET)));
//Load server properties
this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_LOADING));
@@ -122,6 +134,7 @@ public Server(LocalManager localManager, Logger logger, String dataPath) {
this.motd = this.properties.get(ServerPropertiesKeys.MOTD, "A Sculk Server Software");
this.submotd = this.properties.get(ServerPropertiesKeys.SUB_MOTD, "Powered by Sculk");
this.maxPlayers = this.properties.get(ServerPropertiesKeys.MAX_PLAYERS, 20);
+ this.query = this.properties.get(ServerPropertiesKeys.QUERY, false);
this.operators = new SculkOperators();
this.whitelist = new SculkWhitelist();
@@ -132,6 +145,7 @@ public Server(LocalManager localManager, Logger logger, String dataPath) {
this.eventManager = injector.getInstance(EventManager.class);
this.scheduler = injector.getInstance(Scheduler.class);
this.pluginManager = new PluginManager(this);
+ this.resourcePackManager = new ResourcePackManager();
this.simpleCommandMap = new SimpleCommandMap(this);
this.console = new TerminalConsole(this);
@@ -149,7 +163,7 @@ public void start() {
this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_ONLINE_MODE_DISABLED, TextFormat.RED));
}
- log.info(language.translate(LanguageKeys.SCULK_SERVER_STARTING, List.of(TextFormat.DARK_AQUA + CODE_NAME + TextFormat.WHITE, TextFormat.AQUA + CODE_VERSION + TextFormat.WHITE)));
+ log.info(language.translate(LanguageKeys.SCULK_SERVER_STARTING, List.of(TextFormat.DARK_AQUA + CODE_NAME + TextFormat.RESET, TextFormat.AQUA + CODE_VERSION + TextFormat.WHITE)));
InetSocketAddress bindAddress = new InetSocketAddress(this.getProperties().get(ServerPropertiesKeys.SERVER_IP, "0.0.0.0"), this.getProperties().get(ServerPropertiesKeys.SERVER_PORT, 19132));
this.serverId = UUID.randomUUID();
@@ -166,6 +180,8 @@ public void start() {
this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_DISTRIBUTED_UNDER, List.of(TextFormat.AQUA + "GNU GENERAL PUBLIC LICENSE")));
+ this.resourcePackManager.loadResourcePacks();
+
this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_LOADING_COMMANDS));
this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_LOADING_PLUGINS));
@@ -185,13 +201,13 @@ public void start() {
public void shutdown() {
Map onlinePlayers = this.getOnlinePlayers();
+ if (this.shutdown) {
+ return;
+ }
for (Map.Entry entry : onlinePlayers.entrySet()) {
Player player = entry.getValue();
player.getNetworkSession().disconnect(SERVER_CLOSED_MESSAGE);
}
- if (this.shutdown) {
- return;
- }
this.logger.info(this.language.translate(LanguageKeys.SCULK_SERVER_STOPPING));
this.shutdown = true;
@@ -200,8 +216,6 @@ public void shutdown() {
pluginManager.disableAllPlugins();
this.logger.info(this.language.translate(LanguageKeys.SCULK_PLUGINS_DISABLED));
- Sculk.shutdown();
-
this.logger.info(this.language.translate(LanguageKeys.SCULK_NETWORK_INTERFACES_STOPPING));
for (SourceInterface sourceInterface : this.network.getInterfaces()) {
sourceInterface.shutdown();
@@ -222,8 +236,8 @@ public CompletableFuture createPlayer(SculkServerSession session, Client
Player player;
try {
- Constructor extends Player> constructor = clazz.getConstructor(SculkServerSession.class, ClientChainData.class);
- player = constructor.newInstance(session, info);
+ Constructor extends Player> constructor = clazz.getConstructor(Server.class, SculkServerSession.class, ClientChainData.class, NbtMap.class);
+ player = constructor.newInstance(this, session, info, NbtMap.EMPTY);
this.addPlayer(session.getSocketAddress(), player);
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
this.logger.warn(FAILED_TO_CREATE_PLAYER, e);
@@ -262,14 +276,29 @@ public Injector getInjector() {
return injector;
}
+ /**
+ * Checks whether the server is currently running.
+ *
+ * @return {@code true} if the server is running, {@code false} if it has been shut down.
+ */
public boolean isRunning() {
return !this.shutdown;
}
+ /**
+ * Returns the singleton instance of the Server.
+ *
+ * @return the single instance of the Server.
+ */
public static Server getInstance() {
return instance;
}
+ /**
+ * Retrieves the logger instance associated with this class.
+ *
+ * @return the logger instance
+ */
public Logger getLogger() {
return logger;
}
@@ -282,6 +311,12 @@ public Path getPluginDataPath() {
return pluginDataPath;
}
+ /**
+ * Retrieves a map of online players currently active in the system.
+ *
+ * @return An unmodifiable map containing the online players, where the keys are their UUIDs
+ * and the values are the corresponding Player objects.
+ */
public Map getOnlinePlayers() {
return Collections.unmodifiableMap(playerList);
}
@@ -300,71 +335,45 @@ public void broadcastMessage(String message) {
}
}
- public int getMaxPlayers() {
- return maxPlayers;
- }
-
- public String getDefaultGamemode() {
- return defaultGamemode;
- }
-
- public String getMotd() {
- return motd;
+ public Integer getDefaultGamemode() {
+ return this.getProperties().get(ServerPropertiesKeys.GAMEMODE, GameMode.SURVIVAL.getId());
}
public String getSubMotd() {
return submotd;
}
- public UUID getServerId() {
- return serverId;
+ public Operators getOperators() {
+ return this.operators;
}
public void addPlayer(SocketAddress socketAddress, Player player) {
this.players.put(socketAddress, player);
}
- public void addOnlinePlayer(Player player) {
+ public boolean addOnlinePlayer(Player player) {
+ PlayerLoginEvent event = new PlayerLoginEvent(player, "Plugin reason");
+ event.call();
+ if (event.isCancelled() || !player.getNetworkSession().isConnected())
+ return false;
+ for (Player onlinePlayer : this.playerList.values()) {
+ onlinePlayer.getNetworkSession().onPlayerAdded(player);
+ player.spawnTo(onlinePlayer);
+ onlinePlayer.spawnTo(player);
+ }
this.playerList.put(player.getUniqueId(), player);
+ return true;
}
- public void sendFullPlayerList(Player player) {
- PlayerListPacket packet = new PlayerListPacket();
- packet.setAction(PlayerListPacket.Action.ADD);
- packet.getEntries().addAll(this.playerList.values().stream().map(p -> {
- PlayerListPacket.Entry entry = new PlayerListPacket.Entry(p.getUniqueId());
- entry.setEntityId(p.getEntityId());
- entry.setName(p.getName());
- entry.setSkin(SkinUtils.toSerialized(p.getSkin()));
- entry.setPlatformChatId("");
- return entry;
- }).toList());
- player.sendDataPacket(packet);
- }
-
- public void removeFromTabList(Player player) {
- PlayerListPacket packet = new PlayerListPacket();
- packet.setAction(PlayerListPacket.Action.REMOVE);
- packet.getEntries().add(new PlayerListPacket.Entry(player.getUniqueId()));
- broadcastPacket(packet);
+ public void removeOnlinePlayer(Player player) {
+ if (this.playerList.containsKey(player.getUniqueId())) {
+ this.playerList.remove(player.getUniqueId());
+ for (Player onlinePlayer : this.playerList.values()) {
+ onlinePlayer.getNetworkSession().onPlayerRemoved(player);
+ }
+ }
}
- public void addToTabList(UUID uuid, long entityId, String name, ClientChainData chainData, String xuid, Skin skin) {
- PlayerListPacket playerListPacket = new PlayerListPacket();
- playerListPacket.setAction(PlayerListPacket.Action.ADD);
-
- PlayerListPacket.Entry entry = new PlayerListPacket.Entry(uuid);
- entry.setEntityId(entityId);
- entry.setName(name);
- entry.setXuid(xuid);
- entry.setPlatformChatId(chainData.getDeviceId());
- entry.setBuildPlatform(chainData.getDeviceOS());
- entry.setSkin(SkinUtils.toSerialized(skin));
- entry.setTrustedSkin(skin.isTrusted());
-
- playerListPacket.getEntries().add(entry);
- this.broadcastPacket(playerListPacket);
- }
public Scheduler getScheduler() {
return scheduler;
diff --git a/src/main/java/org/sculk/api/player/GameMode.java b/src/main/java/org/sculk/api/player/GameMode.java
new file mode 100644
index 0000000..eb36c5e
--- /dev/null
+++ b/src/main/java/org/sculk/api/player/GameMode.java
@@ -0,0 +1,53 @@
+package org.sculk.api.player;
+
+/*
+ * ____ _ _
+ * / ___| ___ _ _| | | __
+ * \___ \ / __| | | | | |/ /
+ * ___) | (__| |_| | | <
+ * |____/ \___|\__,_|_|_|\_\
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author: SculkTeams
+ * @link: http://www.sculkmp.org/
+ */
+
+public enum GameMode {
+ SURVIVAL("Survival", 0),
+ CREATIVE("Creative", 1),
+ ADVENTURE("Adventure", 2),
+ SPECTATOR("Spectator", 3);
+
+ private final String identifier;
+ private final int id;
+
+ GameMode(String identifier, int id) {
+ this.identifier = identifier;
+ this.id = id;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public static GameMode fromId(int id) {
+ switch (id) {
+ case 0:
+ return SURVIVAL;
+ case 1:
+ return CREATIVE;
+ case 2:
+ return ADVENTURE;
+ default:
+ return SPECTATOR;
+ }
+ }
+}
diff --git a/src/main/java/org/sculk/server/Operators.java b/src/main/java/org/sculk/api/server/Operators.java
similarity index 96%
rename from src/main/java/org/sculk/server/Operators.java
rename to src/main/java/org/sculk/api/server/Operators.java
index 9cf0d29..faf5cf1 100644
--- a/src/main/java/org/sculk/server/Operators.java
+++ b/src/main/java/org/sculk/api/server/Operators.java
@@ -1,4 +1,4 @@
-package org.sculk.server;
+package org.sculk.api.server;
public interface Operators {
diff --git a/src/main/java/org/sculk/server/Whitelist.java b/src/main/java/org/sculk/api/server/Whitelist.java
similarity index 95%
rename from src/main/java/org/sculk/server/Whitelist.java
rename to src/main/java/org/sculk/api/server/Whitelist.java
index e76bf21..0159a83 100644
--- a/src/main/java/org/sculk/server/Whitelist.java
+++ b/src/main/java/org/sculk/api/server/Whitelist.java
@@ -1,4 +1,4 @@
-package org.sculk.server;
+package org.sculk.api.server;
public interface Whitelist {
diff --git a/src/main/java/org/sculk/command/defaults/HelpCommand.java b/src/main/java/org/sculk/command/defaults/HelpCommand.java
index 55d3e22..adb1407 100644
--- a/src/main/java/org/sculk/command/defaults/HelpCommand.java
+++ b/src/main/java/org/sculk/command/defaults/HelpCommand.java
@@ -60,47 +60,48 @@ public void onRun(CommandSender sender, String commandLabel, Map
actualPage = 1;
}
} else if (args.containsKey("command")) {
- Command command = (Command) args.get("command");
+ String commandName = args.get("command").toString();
+ Command command = Server.getInstance().getCommandMap().getCommand(commandName);
if (command != null) {
- sender.sendMessage(new RawTextBuilder().add(new TranslaterBuilder()
+ sender.sendMessage(new RawTextBuilder().add(new TranslaterBuilder()
.setTranslate("§6/%%s: §f%%s\nUsage: §e%%s")
.setWith(new RawTextBuilder()
.add(new TextBuilder().setText(command.getLabel()))
- .add(new TextBuilder().setText(command.getDescription()))
+ .add(new TranslaterBuilder().setTranslate(command.getDescription()))
.add(new TextBuilder().setText(command.getUsageMessage()))
)
));
} else {
- sender.sendMessage(new RawTextBuilder().add(new TranslaterBuilder().setTranslate("§4/%%s§c does not seem to exist, check the list of commands with §4/help§c.")
- .setWith(new RawTextBuilder().add(new TextBuilder().setText(args.get("command").toString())))));
+ sender.sendMessage(new RawTextBuilder().add(new TranslaterBuilder().setTranslate("§4/%%s§c does not seem to exist, check the list of commands with §4/help§c.")
+ .setWith(new RawTextBuilder().add(new TextBuilder().setText(commandName)))));
}
return;
}
-
+ RawTextBuilder commands = RawTextBuilder.create();
int startIndex = (actualPage - 1) * commandsPerPage;
int endIndex = Math.min(startIndex + commandsPerPage, totalCommands);
-
+ TranslaterBuilder commandSentence = new TranslaterBuilder<>();
+ commandSentence.setTranslate("§6/%%s:§f %%s\n");
for (int i = startIndex; i < endIndex; i++) {
String commandName = commandSending.toArray(new String[0])[i];
Command command = Server.getInstance().getCommandMap().getCommand(commandName);
+ if (i == endIndex - 1)
+ commandSentence.setTranslate("§6/%%s:§f %%s");
if (command != null) {
- builder.append("§6/").append(commandName).append(":§f ").append(command.getDescription()).append("\n");
+ TranslaterBuilder clone = commandSentence.clone();
+ commands.add(clone.setWith(RawTextBuilder.create(new TextBuilder().setText(commandName), new TranslaterBuilder<>().setTranslate(command.getDescription()))));
}
}
-
- TranslaterBuilder translaterBuilder = new TranslaterBuilder();
- translaterBuilder.setTranslate("§6-------------- §fHelp - %%s command(s) §7[%%s/%%s] §6--------------\n%%s");
- translaterBuilder.setWith(new RawTextBuilder()
- .add(new TextBuilder()
- .setText(Integer.toString(totalCommands)))
- .add(new TextBuilder()
- .setText(Integer.toString(actualPage)))
- .add(new TextBuilder()
- .setText(Integer.toString(totalPage)))
- .add(new TextBuilder()
- .setText(builder.substring(0, builder.length() - 1)))
+ TranslaterBuilder translaterBuilder = new TranslaterBuilder<>();
+ translaterBuilder.setTranslate("commands.help.header");
+ translaterBuilder.setWith(
+ RawTextBuilder.create(
+ new TextBuilder().setText(Integer.toString(totalCommands)),
+ new TextBuilder().setText(Integer.toString(actualPage)),
+ new TextBuilder().setText(Integer.toString(totalPage)),
+ commands)
);
sender.sendMessage(new RawTextBuilder().add(translaterBuilder));
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/sculk/command/defaults/VersionCommand.java b/src/main/java/org/sculk/command/defaults/VersionCommand.java
index 480512f..4f0c61f 100644
--- a/src/main/java/org/sculk/command/defaults/VersionCommand.java
+++ b/src/main/java/org/sculk/command/defaults/VersionCommand.java
@@ -42,7 +42,7 @@ protected void prepare() {
public void onRun(CommandSender sender, String commandLabel, Map args) {
sender.sendMessage(new RawTextBuilder().add(
new TranslaterBuilder()
- .setTranslate("§fThis server is running §a%%s\n§fServer version: §a%%s\n§fCompatible Minecraft version: §a%%s §f(protocol version: §a%%s§f)\nOperating system: §a%%s")
+ .setTranslate("§fThis server is running §a%s\n§fServer version: §a%s\n§fCompatible Minecraft version: §a%s §f(protocol version: §a%s§f)\nOperating system: §a%s")
.setWith(new RawTextBuilder()
.add(new TextBuilder().setText(Sculk.CODE_NAME)) // software name
.add(new TextBuilder().setText(Sculk.CODE_VERSION)) // software version
diff --git a/src/main/java/org/sculk/config/ServerProperties.java b/src/main/java/org/sculk/config/ServerProperties.java
index ad2cd9d..a59b1ea 100644
--- a/src/main/java/org/sculk/config/ServerProperties.java
+++ b/src/main/java/org/sculk/config/ServerProperties.java
@@ -1,5 +1,7 @@
package org.sculk.config;
+import org.sculk.api.player.GameMode;
+
import java.io.File;
import java.nio.file.Path;
@@ -24,7 +26,7 @@ private ConfigSection getDefaultValues() {
defaults.put(ServerPropertiesKeys.SERVER_PORT.toString(), 19132);
defaults.put(ServerPropertiesKeys.WHITELIST.toString(), "off");
defaults.put(ServerPropertiesKeys.MAX_PLAYERS.toString(), 20);
- defaults.put(ServerPropertiesKeys.GAMEMODE.toString(), 0);
+ defaults.put(ServerPropertiesKeys.GAMEMODE.toString(), GameMode.SURVIVAL.getId());
defaults.put(ServerPropertiesKeys.PVP.toString(), "on");
defaults.put(ServerPropertiesKeys.DIFFICULTY.toString(), 1);
defaults.put(ServerPropertiesKeys.LEVEL_NAME.toString(), "world");
@@ -34,6 +36,8 @@ private ConfigSection getDefaultValues() {
defaults.put(ServerPropertiesKeys.SPAWN_MONSTERS.toString(), "on");
defaults.put(ServerPropertiesKeys.AUTO_SAVE.toString(), "on");
defaults.put(ServerPropertiesKeys.XBOX_AUTH.toString(), "on");
+ defaults.put(ServerPropertiesKeys.QUERY.toString(), "false");
+ defaults.put(ServerPropertiesKeys.FORCE_RESOURCE_PACKS.toString(), "off");
return defaults;
}
diff --git a/src/main/java/org/sculk/config/ServerPropertiesKeys.java b/src/main/java/org/sculk/config/ServerPropertiesKeys.java
index 234a391..7906c9e 100644
--- a/src/main/java/org/sculk/config/ServerPropertiesKeys.java
+++ b/src/main/java/org/sculk/config/ServerPropertiesKeys.java
@@ -17,7 +17,9 @@ public enum ServerPropertiesKeys {
SPAWN_ANIMALS("spawn-animals"),
SPAWN_MONSTERS("spawn-monsters"),
AUTO_SAVE("auto-save"),
- XBOX_AUTH("xbox-auth");
+ XBOX_AUTH("xbox-auth"),
+ QUERY("query"),
+ FORCE_RESOURCE_PACKS("force-resource-packs");
private final String key;
diff --git a/src/main/java/org/sculk/console/ConsoleThread.java b/src/main/java/org/sculk/console/ConsoleThread.java
index 87b54d1..6a37771 100644
--- a/src/main/java/org/sculk/console/ConsoleThread.java
+++ b/src/main/java/org/sculk/console/ConsoleThread.java
@@ -1,7 +1,5 @@
package org.sculk.console;
-import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
-
/*
* ____ _ _
* / ___| ___ _ _| | | __
diff --git a/src/main/java/org/sculk/console/TerminalConsole.java b/src/main/java/org/sculk/console/TerminalConsole.java
index a6503b3..dce1adb 100644
--- a/src/main/java/org/sculk/console/TerminalConsole.java
+++ b/src/main/java/org/sculk/console/TerminalConsole.java
@@ -56,18 +56,17 @@ public Locale getLocale() {
@Override
public Language getLanguage() {
- return null;
+ return this.server.getLanguage();
}
@Override
public void sendMessage(String message) {
this.server.getLogger().info(message);
-
}
@Override
public void sendMessage(RawTextBuilder textBuilder) {
- this.server.getLogger().info(textBuilder.toString());
+ this.server.getLogger().info(this.getLanguage().translate(textBuilder));
}
@Override
diff --git a/src/main/java/org/sculk/entity/Entity.java b/src/main/java/org/sculk/entity/Entity.java
index b3497eb..b7a1685 100644
--- a/src/main/java/org/sculk/entity/Entity.java
+++ b/src/main/java/org/sculk/entity/Entity.java
@@ -1,15 +1,20 @@
package org.sculk.entity;
+import co.aikar.timings.Timing;
+import co.aikar.timings.Timings;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import lombok.Getter;
-import org.checkerframework.checker.units.qual.t;
+import lombok.NonNull;
+import lombok.Setter;
import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector3f;
+import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataMap;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.packet.AddEntityPacket;
+import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.sculk.Server;
import org.sculk.network.broadcaster.EntityEventBroadcaster;
import org.sculk.network.session.SculkServerSession;
@@ -18,7 +23,6 @@
import javax.annotation.Nullable;
import java.util.List;
-import java.util.concurrent.atomic.AtomicLong;
/*
* ____ _ _
@@ -55,7 +59,7 @@ public static long getNextEntityId() {
@Getter
private float health = 20.0F;
- @Getter
+ @Getter @Setter
private int maxHealth = 20;
protected float ySize = 0.0F;
@@ -63,9 +67,10 @@ public static long getNextEntityId() {
public boolean keepMovement = false;
public float fallDistance = 0.0F;
- public int ticksLived = 0;
- public int lastUpdate;
+ public long ticksLived = 0;
+ public long lastUpdate = 0;
protected int fireTicks = 0;
+ protected boolean closed = false;
protected AttributeMap attributeMap;
@@ -76,6 +81,7 @@ public static long getNextEntityId() {
@Getter
private final EntityDataMap networkProperties;
protected boolean networkPropertiesDirty = false;
+ @Getter
protected String nameTag = "";
protected boolean nameTagVisible = true;
protected boolean alwaysShowName = false;
@@ -91,21 +97,111 @@ public static long getNextEntityId() {
protected @Nullable Integer ownerId;
protected @Nullable Integer targetId;
+ protected boolean justCreated = true;
+ protected boolean constructorCalled = false;
public Entity() {
+ if(this.constructorCalled){
+ throw new RuntimeException("Attempted to call constructor for an Entity multiple times");
+ }
+ this.constructorCalled = true;
+ this.entityId = getNextEntityId();
+ this.networkProperties = new EntityDataMap();
+ networkPropertiesDirty = true;
+ this.size = this.getInitialSizeInfo();
+ this.ownerId = null;
+ this.hasSpawned = new ObjectArrayList();
+ this.initEntity(NbtMap.EMPTY);
+ }
+ public Entity(NbtMap nbt) {
+ if(this.constructorCalled){
+ throw new RuntimeException("Attempted to call constructor for an Entity multiple times");
+ }
+ this.constructorCalled = true;
this.entityId = getNextEntityId();
this.networkProperties = new EntityDataMap();
networkPropertiesDirty = true;
this.size = this.getInitialSizeInfo();
this.ownerId = null;
this.hasSpawned = new ObjectArrayList();
+ this.initEntity(nbt);
}
abstract protected EntitySizeInfo getInitialSizeInfo();
- public void initEntity() {
+ protected void initEntity(NbtMap nbt) {
+ }
+
+ public void kill() {
+
+ }
+
+ protected void onDeath() {
+
+ }
+
+ protected boolean onDeathUpdate(long tickDiff)
+ {
+ return true;
+ }
+
+ public boolean isAlive() {
+ return this.health > 0.0F;
+ }
+
+ public void setHealth(float health)
+ {
+ boolean alive;
+ if (health == this.health) {
+ return;
+ }
+ alive = this.isAlive();
+ if (health <= 0.0F) {
+ if (alive && !this.justCreated)
+ {
+ this.kill();
+ }
+ else if (alive)
+ this.health = 0.0F;
+ }else if (health <= this.getMaxHealth() || health > this.health) {
+ this.health = health;
+ } else {
+ this.health = this.getMaxHealth();
+ }
+ }
+
+
+ protected void onFirstUpdate(long $currentTick) {
+ //TODO: EntitySpawnEvent
}
- public void onUpdate() {}
+ public boolean onUpdate(long currentTick) {
+ long tickDiff;
+ boolean hasUpdate;
+ Timing timings;
+ if (this.closed)
+ return false;
+ tickDiff = currentTick - this.lastUpdate;
+ if (tickDiff <= 0.0F) {
+ //TODO: check !justCreated
+ return true;
+ }
+ this.lastUpdate = currentTick;
+ if (this.justCreated)
+ this.onFirstUpdate(currentTick);
+ if (!this.isAlive())
+ {
+ if (this.onDeathUpdate(tickDiff))
+ {
+ //TODO: create flagForDespawn
+ }
+ return true;
+ }
+ timings = Timings.entityBaseTickTimer;
+ timings.startTiming();
+ hasUpdate = this.entityBaseTick(tickDiff);
+ Timings.entityBaseTickTimer.stopTiming();
+ return (hasUpdate);
+ }
public List getViewers()
{
@@ -142,6 +238,24 @@ public void setSize(EntitySizeInfo size) {
this.networkPropertiesDirty = true;
}
+ protected boolean entityBaseTick(long tickDiff) {
+ boolean hasUpdate = false;
+ if (this.justCreated)
+ {
+ this.justCreated = false;
+ if (!this.isAlive())
+ this.kill();
+ }
+ if (networkPropertiesDirty)
+ {
+ this.sendData(this.getNetworkProperties());
+ networkPropertiesDirty = false;
+ }
+
+ this.ticksLived += tickDiff;
+ return hasUpdate;
+ }
+
public boolean isOnFire(){
return (this.fireTicks > 0);
@@ -181,10 +295,51 @@ protected void syncNetworkData(EntityDataMap properties) {
properties.setFlag(EntityFlag.WALL_CLIMBING, this.canClimbWalls);
}
+ public void spawnTo(Player player) {
+ if (!this.hasSpawned.contains(player)) {
+ this.hasSpawned.add(player);
+ this.sendSpawnPacket(player);
+ }
+ }
+
+ public void respawnToAll() {
+ List players = this.getViewers();
+ this.hasSpawned.clear();
+ for (Player player : players) {
+ this.spawnTo(player);
+ }
+ }
+
+ public void despawnFrom(Player player, boolean send) {
+ if (this.hasSpawned.contains(player)) {
+ if(send){
+ player.getNetworkSession().getEntityEventBroadcaster().onEntityRemoved(List.of(player.getNetworkSession()), this);
+ }
+ this.hasSpawned.remove(player);
+ }
+ }
+
+ public void despawnFrom(Player player) {
+ this.despawnFrom(player, true);
+ }
+
+ public void despawnFromAll() {
+ NetworkBroadcastUtils.broadcastEntityEvent(
+ this.getViewers(),
+ (entityEventBroadcaster, sculkServerSessions) -> entityEventBroadcaster.onEntityRemoved(sculkServerSessions, this)
+ );
+ this.hasSpawned.clear();
+ }
+
/**
* Called by spawnTo() to send whatever packets needed to spawn the entity to the client.
*/
protected void sendSpawnPacket(Player player) {
+ player.getNetworkSession().sendPacket(this.createAddEntityPacket());
+ }
+
+ protected @NonNull BedrockPacket createAddEntityPacket()
+ {
AddEntityPacket packet = new AddEntityPacket();
packet.setUniqueEntityId(this.getEntityId());
packet.setRuntimeEntityId(this.getEntityId());
@@ -197,7 +352,7 @@ protected void sendSpawnPacket(Player player) {
packet.setAttributes(this.attributeMap.getAll().values().stream().map(attr -> new AttributeData(attr.getId(), attr.getMinValue(), attr.getMaxValue(), attr.getCurrentValue(), attr.getDefaultValue())).toList());
packet.setMetadata(this.getAllNetworkData());
packet.setEntityLinks(List.of());
- player.getNetworkSession().sendPacket(packet);
+ return packet;
}
public void sendData(List targets, EntityDataMap data) {
diff --git a/src/main/java/org/sculk/player/text/SelectorBuilder.java b/src/main/java/org/sculk/entity/EntityRegistry.java
similarity index 68%
rename from src/main/java/org/sculk/player/text/SelectorBuilder.java
rename to src/main/java/org/sculk/entity/EntityRegistry.java
index 649d975..85ae8fc 100644
--- a/src/main/java/org/sculk/player/text/SelectorBuilder.java
+++ b/src/main/java/org/sculk/entity/EntityRegistry.java
@@ -1,4 +1,5 @@
-package org.sculk.player.text;
+package org.sculk.entity;
+
/*
* ____ _ _
@@ -15,16 +16,8 @@
* @author: SculkTeams
* @link: http://www.sculkmp.org/
*/
-public class SelectorBuilder implements IJsonText {
-
- @Override
- public String getName() {
- return "";
- }
+//TODO: Implement Entity Registry for entities to be registered
- @Override
- public Object build() {
- return null;
- }
-}
\ No newline at end of file
+public class EntityRegistry {
+}
diff --git a/src/main/java/org/sculk/entity/HumanEntity.java b/src/main/java/org/sculk/entity/HumanEntity.java
index a8c2837..62687b3 100644
--- a/src/main/java/org/sculk/entity/HumanEntity.java
+++ b/src/main/java/org/sculk/entity/HumanEntity.java
@@ -2,14 +2,21 @@
import lombok.Getter;
+import lombok.NonNull;
import lombok.Setter;
-import org.cloudburstmc.protocol.bedrock.packet.AddPlayerPacket;
-import org.cloudburstmc.protocol.bedrock.packet.PlayerSkinPacket;
+import org.cloudburstmc.math.vector.Vector3f;
+import org.cloudburstmc.nbt.NbtMap;
+import org.cloudburstmc.protocol.bedrock.data.GameType;
+import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
+import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
+import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
+import org.cloudburstmc.protocol.bedrock.packet.*;
import org.sculk.Server;
import org.sculk.data.bedrock.entity.EntityIds;
import org.sculk.entity.manager.ExperienceManager;
import org.sculk.entity.manager.HungerManager;
import org.sculk.event.player.PlayerChangeSkinEvent;
+import org.sculk.network.session.SculkServerSession;
import org.sculk.network.utils.NetworkBroadcastUtils;
import org.sculk.player.Player;
import org.sculk.player.skin.Skin;
@@ -45,18 +52,22 @@ public class HumanEntity extends Living {
@Setter @Getter
protected Skin skin;
- public HumanEntity(){
- super();
+ public HumanEntity(NbtMap nbt){
+ super(nbt);
this.uuid = UUID.randomUUID();
}
@Override
- public void initEntity() {
- super.initEntity();
+ protected void initEntity(NbtMap nbt) {
+ super.initEntity(nbt);
this.hungerManager = new HungerManager(this);
this.experienceManager = new ExperienceManager(this);
}
+ public String getName(){
+ return this.getNameTag();
+ }
+
public String getNetworkTypeId()
{
return EntityIds.PLAYER;
@@ -96,14 +107,57 @@ public void sendSkin(List target) {
NetworkBroadcastUtils.broadcastPackets(target, List.of(skinPacket));
}
+ @Override
+ public void spawnTo(Player player) {
+ if (this != player) {
+ super.spawnTo(player);
+ }
+ }
+
@Override
protected void sendSpawnPacket(Player player) {
- //TODO
+ SculkServerSession networkSession = player.getNetworkSession();
+ if(!(this instanceof Player)) {
+ PlayerListPacket packet = new PlayerListPacket();
+ packet.setAction(PlayerListPacket.Action.ADD);
+ packet.getEntries().add(networkSession.createPlayerEntry(this, true));
+ networkSession.sendPacket(packet);
+ }
+ AddPlayerPacket addPlayerPacket = createAddEntityPacket();
+ networkSession.sendPacket(addPlayerPacket);
+
+ if(!(this instanceof Player)) {
+ PlayerListPacket packet = new PlayerListPacket();
+ packet.setAction(PlayerListPacket.Action.REMOVE);
+ packet.getEntries().add(networkSession.createPlayerEntry(this, false));
+ networkSession.sendPacket(packet);
+ }
}
@Override
- public void onUpdate() {
- super.onUpdate();
+ protected @NonNull BedrockPacket createAddEntityPacket() {
+ AddPlayerPacket addPlayerPacket = new AddPlayerPacket();
+ addPlayerPacket.setUuid(this.getUniqueId());
+ addPlayerPacket.setUsername(this.getName());
+ addPlayerPacket.setRuntimeEntityId(this.getEntityId());
+ addPlayerPacket.setUniqueEntityId(this.getEntityId());
+ addPlayerPacket.setPlatformChatId("");
+ addPlayerPacket.setPosition(Vector3f.ZERO);
+ addPlayerPacket.setRotation(Vector3f.ZERO);
+ addPlayerPacket.setMotion(Vector3f.ZERO);
+ addPlayerPacket.setHand(ItemData.AIR);
+ addPlayerPacket.getAdventureSettings().setCommandPermission(CommandPermission.ANY);
+ addPlayerPacket.getAdventureSettings().setPlayerPermission(PlayerPermission.MEMBER);
+ addPlayerPacket.setGameType(GameType.SURVIVAL);
+ addPlayerPacket.setMetadata(this.getAllNetworkData());
+ addPlayerPacket.setDeviceId("");
+ addPlayerPacket.setBuildPlatform(0);
+ return addPlayerPacket;
}
+
+ @Override
+ public boolean onUpdate(long currentTick) {
+ return super.onUpdate(currentTick);
+ }
}
diff --git a/src/main/java/org/sculk/entity/Living.java b/src/main/java/org/sculk/entity/Living.java
index 4328de5..ec5d9a2 100644
--- a/src/main/java/org/sculk/entity/Living.java
+++ b/src/main/java/org/sculk/entity/Living.java
@@ -1,6 +1,7 @@
package org.sculk.entity;
+import org.cloudburstmc.nbt.NbtMap;
import org.sculk.entity.manager.HungerManager;
/*
@@ -20,13 +21,17 @@
*/
public abstract class Living extends Entity {
+ public Living(NbtMap nbt) {
+ super(nbt);
+ }
+
@Override
- public void initEntity() {
- super.initEntity();
+ protected void initEntity(NbtMap nbt) {
+ super.initEntity(nbt);
}
@Override
- public void onUpdate() {
- super.onUpdate();
+ public boolean onUpdate(long currentTick) {
+ return super.onUpdate(currentTick);
}
}
diff --git a/src/main/java/org/sculk/event/player/PlayerJoinEvent.java b/src/main/java/org/sculk/event/player/PlayerJoinEvent.java
index 87f404e..9f58ad0 100644
--- a/src/main/java/org/sculk/event/player/PlayerJoinEvent.java
+++ b/src/main/java/org/sculk/event/player/PlayerJoinEvent.java
@@ -1,6 +1,7 @@
package org.sculk.event.player;
+import lombok.NonNull;
import org.sculk.player.Player;
/*
@@ -20,18 +21,19 @@
*/
public class PlayerJoinEvent extends PlayerEvent {
+ @NonNull
protected String joinMessage;
- public PlayerJoinEvent(Player player, String joinMessage) {
+ public PlayerJoinEvent(Player player, @NonNull String joinMessage) {
super(player);
this.joinMessage = joinMessage;
}
- public String getJoinMessage() {
+ public @NonNull String getJoinMessage() {
return joinMessage;
}
- public void setJoinMessage(String joinMessage) {
+ public void setJoinMessage(@NonNull String joinMessage) {
this.joinMessage = joinMessage;
}
}
diff --git a/src/main/java/org/sculk/event/player/PlayerLoginEvent.java b/src/main/java/org/sculk/event/player/PlayerLoginEvent.java
new file mode 100644
index 0000000..fd55124
--- /dev/null
+++ b/src/main/java/org/sculk/event/player/PlayerLoginEvent.java
@@ -0,0 +1,32 @@
+package org.sculk.event.player;
+
+
+import lombok.Getter;
+import lombok.Setter;
+import org.sculk.event.Cancellable;
+import org.sculk.player.Player;
+
+/*
+ * ____ _ _
+ * / ___| ___ _ _| | | __
+ * \___ \ / __| | | | | |/ /
+ * ___) | (__| |_| | | <
+ * |____/ \___|\__,_|_|_|\_\
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author: SculkTeams
+ * @link: http://www.sculkmp.org/
+ */
+public class PlayerLoginEvent extends PlayerEvent implements Cancellable {
+
+ @Getter @Setter
+ private Object kickMessage;
+
+ public PlayerLoginEvent(Player player, Object message) {
+ super(player);
+ }
+}
diff --git a/src/main/java/org/sculk/event/server/QueryHandlerEvent.java b/src/main/java/org/sculk/event/server/QueryHandlerEvent.java
new file mode 100644
index 0000000..ca4c6e7
--- /dev/null
+++ b/src/main/java/org/sculk/event/server/QueryHandlerEvent.java
@@ -0,0 +1,55 @@
+package org.sculk.event.server;
+
+
+import lombok.Getter;
+import lombok.Setter;
+import org.sculk.player.Player;
+
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+/*
+ * ____ _ _
+ * / ___| ___ _ _| | | __
+ * \___ \ / __| | | | | |/ /
+ * ___) | (__| |_| | | <
+ * |____/ \___|\__,_|_|_|\_\
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author: SculkTeams
+ * @link: http://www.sculkmp.org/
+ */
+public class QueryHandlerEvent extends ServerEvent {
+ @Getter @Setter
+ private String motd;
+ @Getter @Setter
+ private String smp;
+ @Getter @Setter
+ private String mcpe;
+ @Getter @Setter
+ private String s;
+ @Getter @Setter
+ private Collection values;
+ @Getter @Setter
+ private int maxPlayers;
+ @Getter @Setter
+ private String waterdogPE;
+ @Getter @Setter
+ private InetSocketAddress address;
+
+ public QueryHandlerEvent(String motd, String smp, String mcpe, String s, Collection values, int maxPlayers, String waterdogPE, InetSocketAddress address) {
+ super();
+ this.motd = motd;
+ this.smp = smp;
+ this.mcpe = mcpe;
+ this.s = s;
+ this.values = values;
+ this.maxPlayers = maxPlayers;
+ this.waterdogPE = waterdogPE;
+ this.address = address;
+ }
+}
diff --git a/src/main/java/org/sculk/event/server/ServerEvent.java b/src/main/java/org/sculk/event/server/ServerEvent.java
new file mode 100644
index 0000000..ec7e953
--- /dev/null
+++ b/src/main/java/org/sculk/event/server/ServerEvent.java
@@ -0,0 +1,24 @@
+package org.sculk.event.server;
+
+
+import org.sculk.Server;
+import org.sculk.event.Event;
+
+/*
+ * ____ _ _
+ * / ___| ___ _ _| | | __
+ * \___ \ / __| | | | | |/ /
+ * ___) | (__| |_| | | <
+ * |____/ \___|\__,_|_|_|\_\
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * @author: SculkTeams
+ * @link: http://www.sculkmp.org/
+ */
+public class ServerEvent extends Event {
+
+}
diff --git a/src/main/java/org/sculk/lang/Language.java b/src/main/java/org/sculk/lang/Language.java
index e0cdad4..bbc74fc 100644
--- a/src/main/java/org/sculk/lang/Language.java
+++ b/src/main/java/org/sculk/lang/Language.java
@@ -2,12 +2,16 @@
import lombok.Getter;
import lombok.NonNull;
-import lombok.Setter;
+import org.sculk.player.text.IJsonText;
+import org.sculk.player.text.RawTextBuilder;
+import org.sculk.player.text.TextBuilder;
+import org.sculk.player.text.TranslaterBuilder;
import javax.annotation.Nullable;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/*
* ____ _ _
@@ -32,23 +36,38 @@ public class Language {
@Getter
private @NonNull Map translate;
+ private static final Pattern PATTERN_STRING_EXTERNAL = Pattern.compile("%%[sd]");
+ private static final Pattern PATTERN_INDEX_EXTERNAL = Pattern.compile("%%(\\d+)(\\$[sd])|%%");
+ private static final Pattern PATTERN_INTERNAL = Pattern.compile("(? translate) {
this.name = name;
this.locale = locale;
- this.translate = translate;
+ this.translate = new HashMap<>();
+ translate.forEach((key, value) -> {
+ this.translate.put(key, reformatConfigToMinecraft(value));
+ });
+ translate.clear();
+ }
+
+ public static String reformatConfigToMinecraft(String value){
+ StringBuilder result = new StringBuilder();
+ Matcher matcher = PATTERN_INTERNAL.matcher(value);
+ while (matcher.find()) {
+ matcher.appendReplacement(result, "%" + Matcher.quoteReplacement(matcher.group()));
+ }
+ matcher.appendTail(result);
+ return result.toString().replace("\\n" , "\n");
}
public final @Nullable String internalGet(String key){
- String value = translate.getOrDefault(key, null);
- if (value != null)
- return value.replace("\\n", "\n");
- return null;
+ return translate.getOrDefault(key, null);
}
public String translate(String str, @Nullable List