diff --git a/build.gradle b/build.gradle index 3272b6a..2c39fcc 100644 --- a/build.gradle +++ b/build.gradle @@ -10,8 +10,6 @@ group 'eu.mhsl.minenet' version '1.0-SNAPSHOT' repositories { - //maven 'https://repo.unnamed.team/repository/unnamed-public/' - mavenCentral() google() @@ -46,7 +44,7 @@ dependencies { testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' //https://jitpack.io/#Minestom/Minestom - implementation 'net.minestom:minestom-snapshots:59406d5b54' + implementation 'net.minestom:minestom-snapshots:fd51c8d17a' //Tools implementation 'de.articdive:jnoise:3.0.2' @@ -55,11 +53,11 @@ dependencies { implementation 'org.spongepowered:configurate-yaml:4.1.2' implementation 'com.sparkjava:spark-core:2.9.4' implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.google.guava:guava:32.0.0-android' + implementation 'com.google.guava:guava:32.0.1-android' //PvP - implementation 'com.github.TogAr2:MinestomPvP:04180ddf9a' + implementation 'io.github.TogAr2:MinestomPvP:PR62-SNAPSHOT' // Hephaestus engine implementation("team.unnamed:hephaestus-api:0.2.1-SNAPSHOT") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87..9355b41 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 7030ffb..f5feea6 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -25,7 +27,7 @@ # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is # noncompliant, but you have some other compliant shell such as ksh or # bash, then to run this script, type that shell name before the whole -# eu.mhsl.minenet.minigames.command line, like: +# command line, like: # # ksh Gradle # @@ -35,7 +37,7 @@ # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», # «${var#prefix}», «${var%suffix}», and «$( cmd )»; # * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «eu.mhsl.minenet.minigames.command», «set», and «ulimit». +# * various built-in commands including «command», «set», and «ulimit». # # Important for patching: # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,12 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -117,7 +118,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar -# Determine the Java eu.mhsl.minenet.minigames.command to use to start the JVM. +# Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables @@ -133,29 +134,36 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac fi -# Collect all arguments for the java eu.mhsl.minenet.minigames.command, stacking in reverse order: -# * args from the eu.mhsl.minenet.minigames.command line +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line # * the main class name # * -classpath # * -D...appname settings @@ -193,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java eu.mhsl.minenet.minigames.command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ @@ -205,6 +217,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. @@ -214,7 +232,7 @@ set -- \ # readarray ARGS < <( xargs -n1 <<<"$var" ) && # set -- "${ARGS[@]}" "$@" # -# but POSIX shell has neither arrays nor eu.mhsl.minenet.minigames.command substitution, so instead we +# but POSIX shell has neither arrays nor command substitution, so instead we # post-process each arg (as a line of input to sed) to backslash-escape any # character that might be a shell metacharacter, then use eval to reverse # that process (while maintaining the separation between arguments), and wrap diff --git a/gradlew.bat b/gradlew.bat index bb36ea6..9d21a21 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' eu.mhsl.minenet.gameList.command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,16 +59,16 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail :execute -@rem Setup the eu.mhsl.minenet.gameList.command line +@rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/src/main/java/eu/mhsl/minenet/minigames/Main.java b/src/main/java/eu/mhsl/minenet/minigames/Main.java index dbacc22..a04111a 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/Main.java +++ b/src/main/java/eu/mhsl/minenet/minigames/Main.java @@ -5,8 +5,6 @@ import eu.mhsl.minenet.minigames.command.Commands; import eu.mhsl.minenet.minigames.handler.Listeners; import eu.mhsl.minenet.minigames.lang.Languages; import eu.mhsl.minenet.minigames.server.tasks.TablistUpdateTask; -import eu.mhsl.minenet.minigames.server.provider.ByPlayerNameUuidProvider; -import io.github.togar2.pvp.MinestomPvP; import net.minestom.server.MinecraftServer; import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.extras.lan.OpenToLAN; @@ -42,14 +40,12 @@ public class Main { logger.info("Initialize Minecraft server..."); MinecraftServer server = MinecraftServer.init(); - MinestomPvP.init(); +// MinestomPvP.init(); MinecraftServer.setBrandName("mhsl.eu - minenet - credits to minestom"); MinecraftServer.setCompressionThreshold(serverConfig.node("compression-threshold").getInt(0)); System.setProperty("minestom.chunk-view-distance", String.valueOf(serverConfig.node("view-distance").getInt())); - MinecraftServer.getConnectionManager().setUuidProvider(new ByPlayerNameUuidProvider()); - Commands.values(); Listeners.values(); new HttpServer(); diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java b/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java index a15aba4..a9a6a90 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/Commands.java @@ -1,6 +1,6 @@ package eu.mhsl.minenet.minigames.command; -import eu.mhsl.minenet.minigames.command.anonymous.SkinCommand; +import eu.mhsl.minenet.minigames.command.privileged.SkinCommand; import eu.mhsl.minenet.minigames.command.privileged.*; import eu.mhsl.minenet.minigames.command.anonymous.HubCommand; import eu.mhsl.minenet.minigames.command.anonymous.LeaveCommand; @@ -41,7 +41,7 @@ public enum Commands { static { MinecraftServer.getCommandManager().setUnknownCommandCallback((sender, command) -> { if(command.isBlank()) return; - new ChatMessage(Icon.ERROR).appendStatic("Unknown command").quote(command).send(sender); + new ChatMessage(Icon.ERROR).appendStatic("Unknown command: ").quote(command).send(sender); }); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/PrivilegedCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/PrivilegedCommand.java index 13294f6..c4aa335 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/PrivilegedCommand.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/PrivilegedCommand.java @@ -2,6 +2,7 @@ package eu.mhsl.minenet.minigames.command; import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.condition.CommandCondition; +import net.minestom.server.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,7 +28,7 @@ public class PrivilegedCommand extends Command { } protected CommandCondition isPrivileged() { - return (sender, commandString) -> sender.hasPermission("admin"); + return (sender, commandString) -> ((Player) sender).getPermissionLevel() == 4; } protected void addCondition(CommandCondition condition) { diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/anonymous/HubCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/anonymous/HubCommand.java index 2440dd9..9936895 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/anonymous/HubCommand.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/anonymous/HubCommand.java @@ -10,9 +10,13 @@ public class HubCommand extends Command { public HubCommand() { super("hub"); - setCondition((sender, commandString) -> ((Player) sender).getInstance() instanceof Room); + setCondition( + (sender, commandString) -> + ((Player) sender).getInstance() instanceof Room room && !room.apiDriven + ); setDefaultExecutor((sender, context) -> { + if(Room.getRoom((Player) sender).orElseThrow().apiDriven) return; Room.unsetRoom((Player) sender); MoveInstance.move((Player) sender, Hub.INSTANCE); }); diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/DebugCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/DebugCommand.java index 4e78ce2..4298529 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/DebugCommand.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/DebugCommand.java @@ -5,7 +5,6 @@ import eu.mhsl.minenet.minigames.message.Icon; import eu.mhsl.minenet.minigames.message.type.ActionBarMessage; import eu.mhsl.minenet.minigames.message.type.ChatMessage; import eu.mhsl.minenet.minigames.message.type.TitleMessage; -import eu.mhsl.minenet.minigames.util.PacketUtil; import net.minestom.server.entity.Player; import java.util.ArrayList; @@ -36,8 +35,6 @@ public class DebugCommand extends PrivilegedCommand { .appendTranslated("score#thanks") .send(sender); - PacketUtil.resendPlayerList(((Player) sender)); - new ChatMessage(Icon.SCIENCE).appendStatic(((Player) sender).getUuid().toString()).send(sender); }); diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/OpCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/OpCommand.java index 8b84d16..aebc0d7 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/OpCommand.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/OpCommand.java @@ -6,7 +6,6 @@ import eu.mhsl.minenet.minigames.message.type.ChatMessage; import net.minestom.server.MinecraftServer; import net.minestom.server.command.builder.arguments.ArgumentType; import net.minestom.server.entity.Player; -import net.minestom.server.permission.Permission; public class OpCommand extends PrivilegedCommand { public OpCommand() { @@ -15,7 +14,7 @@ public class OpCommand extends PrivilegedCommand { addSyntax((sender, context) -> { Player target = MinecraftServer.getConnectionManager().getOnlinePlayerByUsername(context.getRaw("target")); if(target != null) { - target.addPermission(new Permission("admin")); + target.setPermissionLevel(4); target.refreshCommands(); } else new ChatMessage(Icon.ERROR).appendStatic("Spieler nicht gefunden").send(sender); }, ArgumentType.Entity("target").onlyPlayers(true)); diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/PublishRewardCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/PublishRewardCommand.java index b72ea14..3313581 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/privileged/PublishRewardCommand.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/PublishRewardCommand.java @@ -24,7 +24,7 @@ public class PublishRewardCommand extends PrivilegedCommand { String rewardRequestJson = new Gson().toJson(room.getTournament().getRewards()); HttpRequest giveRewardsRequest = HttpRequest.newBuilder() - .uri(new URI("http://10.20.6.1:8080/api/event/reward")) + .uri(new URI("http://10.20.7.1:8080/api/event/reward")) .POST(HttpRequest.BodyPublishers.ofString(rewardRequestJson)) .build(); diff --git a/src/main/java/eu/mhsl/minenet/minigames/command/anonymous/SkinCommand.java b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/SkinCommand.java similarity index 73% rename from src/main/java/eu/mhsl/minenet/minigames/command/anonymous/SkinCommand.java rename to src/main/java/eu/mhsl/minenet/minigames/command/privileged/SkinCommand.java index 9f4ffe1..4de09dc 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/command/anonymous/SkinCommand.java +++ b/src/main/java/eu/mhsl/minenet/minigames/command/privileged/SkinCommand.java @@ -1,11 +1,11 @@ -package eu.mhsl.minenet.minigames.command.anonymous; +package eu.mhsl.minenet.minigames.command.privileged; -import net.minestom.server.command.builder.Command; +import eu.mhsl.minenet.minigames.command.PrivilegedCommand; import net.minestom.server.command.builder.arguments.ArgumentType; import net.minestom.server.entity.Player; import net.minestom.server.entity.PlayerSkin; -public class SkinCommand extends Command { +public class SkinCommand extends PrivilegedCommand { public SkinCommand() { super("skin"); diff --git a/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java b/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java index f59fa10..72b17c0 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java +++ b/src/main/java/eu/mhsl/minenet/minigames/handler/Listeners.java @@ -6,7 +6,6 @@ import net.minestom.server.event.EventListener; public enum Listeners { SPAWN(new AddEntityToInstanceEventListener()), - CHAT(new PlayerChatHandler()), LOGIN(new PlayerLoginHandler()), LEAVE(new PlayerLeaveHandler()); diff --git a/src/main/java/eu/mhsl/minenet/minigames/handler/global/AddEntityToInstanceEventListener.java b/src/main/java/eu/mhsl/minenet/minigames/handler/global/AddEntityToInstanceEventListener.java index c52c161..358eea8 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/handler/global/AddEntityToInstanceEventListener.java +++ b/src/main/java/eu/mhsl/minenet/minigames/handler/global/AddEntityToInstanceEventListener.java @@ -1,7 +1,6 @@ package eu.mhsl.minenet.minigames.handler.global; import eu.mhsl.minenet.minigames.instance.Spawnable; -import eu.mhsl.minenet.minigames.util.PacketUtil; import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.event.EventListener; @@ -26,9 +25,7 @@ public class AddEntityToInstanceEventListener implements EventListener { - @Override - public @NotNull Class eventType() { - return PlayerChatEvent.class; - } - - @Override - public @NotNull Result run(@NotNull PlayerChatEvent event) { - event.setChatFormat( - (messages) -> new ChatMessage() - .appendStatic(event.getPlayer().getUsername()) - .pipe() - .appendStatic(messages.getMessage()) - .build(event.getPlayer()) - ); - - return Result.SUCCESS; - } -} diff --git a/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java b/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java index 37c2b2d..b694668 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java +++ b/src/main/java/eu/mhsl/minenet/minigames/handler/global/PlayerLoginHandler.java @@ -11,7 +11,8 @@ import net.minestom.server.entity.Player; import eu.mhsl.minenet.minigames.instance.hub.Hub; import net.minestom.server.event.EventListener; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; -import net.minestom.server.permission.Permission; +import net.minestom.server.network.packet.server.play.TeamsPacket; +import net.minestom.server.scoreboard.Team; import net.minestom.server.timer.TaskSchedule; import org.jetbrains.annotations.NotNull; import org.spongepowered.configurate.serialize.SerializationException; @@ -21,6 +22,11 @@ import java.util.UUID; import java.util.logging.Logger; public class PlayerLoginHandler implements EventListener { + public static final Team globalTeam = MinecraftServer.getTeamManager() + .createBuilder("global") + .collisionRule(TeamsPacket.CollisionRule.NEVER) + .build(); + @Override public @NotNull Class eventType() { return AsyncPlayerConfigurationEvent.class; @@ -37,22 +43,23 @@ public class PlayerLoginHandler implements EventListener { - if(pushQueue != null) { - Room.setRoom(p, Room.getRoom(pushQueue).orElseThrow()); - } else { - MoveInstance.move(p, Hub.INSTANCE); - } - }, - TaskSchedule.seconds(5), - TaskSchedule.stop() + () -> { + p.setTeam(globalTeam); + if(pushQueue != null) { + Room.setRoom(p, Room.getRoom(pushQueue).orElseThrow()); + } else { + MoveInstance.move(p, Hub.INSTANCE); + } + }, + TaskSchedule.seconds(1), + TaskSchedule.stop() ); SkinCache.applySkin(p); try { if(Objects.requireNonNull(Main.globalConfig.node("admins").getList(String.class)).stream().anyMatch(s -> s.equalsIgnoreCase(p.getUsername()))) { - p.addPermission(new Permission("admin")); + p.setPermissionLevel(4); } } catch (SerializationException | NullPointerException ignored) {} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/Game.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/Game.java index dfae886..9133eb8 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/Game.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/Game.java @@ -42,13 +42,13 @@ public abstract class Game extends MineNetInstance implements Spawnable { MinecraftServer.getInstanceManager().registerInstance(this); - logger = Logger.getLogger("Game:" + getUniqueId()); + logger = Logger.getLogger("Game:" + getUuid()); eventNode() - .addListener(PlayerMoveEvent.class, this::onPlayerMove) - .addListener(PlayerBlockBreakEvent.class, this::onBlockBreak) - .addListener(PlayerBlockPlaceEvent.class, this::onBlockPlace) - .addListener(ItemDropEvent.class, this::onItemDrop); + .addListener(PlayerMoveEvent.class, this::onPlayerMove) + .addListener(PlayerBlockBreakEvent.class, this::onBlockBreak) + .addListener(PlayerBlockPlaceEvent.class, this::onBlockPlace) + .addListener(ItemDropEvent.class, this::onItemDrop); } public Game setParent(Room parentRoom) { @@ -63,6 +63,17 @@ public abstract class Game extends MineNetInstance implements Spawnable { game.load(); Room.getRoom(owner).orElseThrow().moveMembersToInstance(game); + MinecraftServer.getSchedulerManager().scheduleTask(() -> { + game.getPlayers().forEach(player -> new ChatMessage(Icon.SCIENCE) + .appendStatic(factory.name().getAssembled(player).asComponent()) + .newLine() + .appendStatic(factory.description().getAssembled(player).asComponent()) + .send(player)); + + return TaskSchedule.stop(); + }, TaskSchedule.seconds(3)); + + } catch (Exception e) { new ChatMessage(Icon.ERROR).appendStatic("Instance crashed: " + e.getMessage()).send(owner); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> Room.getRoom(owner).orElseThrow().moveMembersToRoomLobby()); @@ -102,7 +113,7 @@ public abstract class Game extends MineNetInstance implements Spawnable { scheduler().scheduleTask(() -> { - logger.info("stopping game instance " + this.uniqueId); + logger.info("stopping game instance " + this.uuid); getPlayers().forEach(player -> player.kick("timeout")); MinecraftServer.getInstanceManager().unregisterInstance(this); diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java index 5c0cc63..fe2fa7c 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/GameList.java @@ -8,9 +8,11 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.backrooms.Backroo import eu.mhsl.minenet.minigames.instance.game.stateless.types.bedwars.BedwarsFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain.AcidRainFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.deathcube.DeathcubeFactory; +import eu.mhsl.minenet.minigames.instance.game.stateless.types.jumpDive.JumpDiveFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.minerun.MinerunFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.spleef.SpleefFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.stickfight.StickFightFactory; +import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.TetrisFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.test.Test; import eu.mhsl.minenet.minigames.instance.game.stateless.types.test.TestFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.tntrun.TntRunFactory; @@ -19,18 +21,20 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.trafficlightrace. public enum GameList { DEATHCUBE(new DeathcubeFactory(), GameType.JUMPNRUN), - STICKFIGHT(new StickFightFactory(), GameType.PVP), MINERUN(new MinerunFactory(), GameType.JUMPNRUN), TRAFFICLIGHTRACE(new TrafficLightRaceFactory(), GameType.OTHER), + STICKFIGHT(new StickFightFactory(), GameType.PROTOTYPE), TOWERDEFENSE(new TowerdefenseFactory(), GameType.PROTOTYPE), BEDWARS(new BedwarsFactory(), GameType.PROTOTYPE), BACKROOMS(new BackroomsFactory(), GameType.PROTOTYPE), + BOWSPLEEF(new BowSpleefFactory(), GameType.PROTOTYPE), + TETRIS(new TetrisFactory(), GameType.OTHER), TNTRUN(new TntRunFactory(), GameType.OTHER), - ACIDRAIN(new AcidRainFactory(), GameType.PVE), ANVILRUN(new AnvilRunFactory(), GameType.PVE), + ACIDRAIN(new AcidRainFactory(), GameType.PVE), ELYTRARACE(new ElytraRaceFactory(), GameType.PVP), SPLEEF(new SpleefFactory(), GameType.PVP), - BOWSPLEEF(new BowSpleefFactory(), GameType.PVP), + JUMPDIVE(new JumpDiveFactory(), GameType.JUMPNRUN), Test(new TestFactory(), GameType.OTHER); private final GameFactory factory; diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java index c9efbb0..e318dd5 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/StatelessGame.java @@ -11,6 +11,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.timer.ExecutionType; +import net.minestom.server.timer.Task; import net.minestom.server.timer.TaskSchedule; import net.minestom.server.world.DimensionType; @@ -20,6 +21,7 @@ import java.util.concurrent.CompletableFuture; public class StatelessGame extends Game { private final String name; private final Score score; + private Task timeLimitTask; private int timeLimit = 0; private int timePlayed = 0; @@ -40,8 +42,12 @@ public class StatelessGame extends Game { public void setTimeLimit(int limit) { this.timeLimit = limit; - if(timeLimit > 0) { - scheduler().submitTask(() -> { + if(this.timeLimitTask != null) { + this.timeLimitTask.cancel(); + this.timePlayed = 0; + } + if(this.timeLimit > 0) { + this.timeLimitTask = scheduler().submitTask(() -> { if(!isRunning || timeLimit == 0) return TaskSchedule.stop(); if(timeLimit <= timePlayed) { stop(); @@ -50,8 +56,8 @@ public class StatelessGame extends Game { int timeLeft = timeLimit - timePlayed; switch (timeLeft) { - case 60, 30, 10, 5, 4, 3, 2, 1 -> - new ChatMessage(Icon.SCIENCE).appendStatic("Noch " + timeLeft + " Sekunden!").send(getPlayers()); + case 90, 60, 30, 10, 5, 4, 3, 2, 1 -> + new ChatMessage(Icon.SCIENCE).appendStatic("Noch " + timeLeft + " Sekunden!").send(getPlayers()); } timePlayed++; diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/Option.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/Option.java index 26d9959..1c6fc8f 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/Option.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/Option.java @@ -9,6 +9,7 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import java.util.List; +import java.util.Objects; public abstract class Option { private RestrictionHandler restrictionHandler; @@ -25,7 +26,7 @@ public abstract class Option { this.name = name; this.options = options; - currentValue = options.get(0); + currentValue = options.getFirst(); } public void setRestrictionHandler(RestrictionHandler restrictionHandler) { @@ -44,11 +45,11 @@ public abstract class Option { } public ItemStack getCurrent(Player p) { - int amount = Integer.parseInt(options.get(pointer).toString()); + String value = options.get(pointer).toString(); return ItemStack.builder(item) .customName(name.getAssembled(p)) .lore(TranslatedComponent.byId("optionCommon#value").setColor(NamedTextColor.GOLD).getAssembled(p) - .append(Component.text(": ")).append(Component.text(amount))) + .append(Component.text(": ")).append(Component.text(value))) .build(); } @@ -56,6 +57,10 @@ public abstract class Option { return Integer.parseInt(getAsString()); } + public boolean getAsBoolean() { + return Objects.equals(getAsString(), "true") || Objects.equals(getAsString(), "1"); + } + public String getAsString() { return currentValue.toString(); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/common/BoolOption.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/common/BoolOption.java index d77ba96..ae9548f 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/common/BoolOption.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/config/common/BoolOption.java @@ -6,8 +6,8 @@ import net.minestom.server.item.Material; import java.util.List; -public class BoolOption extends Option { +public class BoolOption extends Option { public BoolOption(String id, Material item, TranslatedComponent name) { - super(id, item, name, List.of(true, false)); + super(id, item, name, List.of("true", "false")); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/acidRain/AcidRainFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/acidRain/AcidRainFactory.java index 3f6964b..bc189d4 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/acidRain/AcidRainFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/acidRain/AcidRainFactory.java @@ -15,6 +15,11 @@ public class AcidRainFactory implements GameFactory { return TranslatedComponent.byId("game_AcidRain#name"); } + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_AcidRain#description"); + } + @Override public Material symbol() { return Material.SLIME_BALL; diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleef.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleef.java index 402256d..bbb7d3b 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleef.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleef.java @@ -6,31 +6,22 @@ import eu.mhsl.minenet.minigames.message.component.TranslatedComponent; import eu.mhsl.minenet.minigames.score.LastWinsScore; import eu.mhsl.minenet.minigames.util.BatchUtil; import eu.mhsl.minenet.minigames.util.GeneratorUtils; -import io.github.togar2.pvp.entity.projectile.CustomEntityProjectile; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.*; import net.minestom.server.entity.metadata.other.PrimedTntMeta; -import net.minestom.server.entity.metadata.projectile.ArrowMeta; import net.minestom.server.event.EventListener; import net.minestom.server.event.entity.projectile.ProjectileCollideWithBlockEvent; import net.minestom.server.event.entity.projectile.ProjectileCollideWithEntityEvent; -import net.minestom.server.event.item.ItemUpdateStateEvent; -import net.minestom.server.event.player.PlayerItemAnimationEvent; import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.block.Block; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; -import net.minestom.server.item.enchant.Enchantment; import net.minestom.server.tag.Tag; -import net.minestom.server.utils.MathUtils; -import net.minestom.server.utils.time.TimeUnit; import org.jetbrains.annotations.NotNull; -import java.time.Duration; -import java.util.Objects; import java.util.concurrent.CompletableFuture; public class BowSpleef extends StatelessGame { @@ -41,40 +32,40 @@ public class BowSpleef extends StatelessGame { public BowSpleef() { super(Dimension.OVERWORLD.key, "bowSpleef", new LastWinsScore()); - eventNode().addListener( - EventListener - .builder(PlayerItemAnimationEvent.class) - .handler(playerItemAnimationEvent -> playerItemAnimationEvent.getPlayer().setTag(CHARGE_BOW_SINCE, System.currentTimeMillis())) - .filter(playerItemAnimationEvent -> playerItemAnimationEvent.getItemAnimationType() == PlayerItemAnimationEvent.ItemAnimationType.BOW) - .build() - ); - - eventNode().addListener( - EventListener - .builder(ItemUpdateStateEvent.class) - .handler(event -> { - final Player player = event.getPlayer(); - final double chargedFor = (System.currentTimeMillis() - player.getTag(CHARGE_BOW_SINCE)) / 1000D; - final double power = MathUtils.clamp((chargedFor * chargedFor + 2 * chargedFor) / 2D, 0, 1); - - if (power > 0.2) { - final CustomEntityProjectile projectile = new CustomEntityProjectile(player, EntityType.ARROW); - final ArrowMeta meta = (ArrowMeta) projectile.getEntityMeta(); - meta.setCritical(power >= 0.9); - projectile.scheduleRemove(Duration.of(100, TimeUnit.SERVER_TICK)); - meta.setOnFire(true); - - final Pos position = player.getPosition().add(0, player.getEyeHeight(), 0); - projectile.setInstance(Objects.requireNonNull(player.getInstance()), position); - - final Vec direction = projectile.getPosition().direction(); - projectile.shootFrom(position.add(direction).sub(0, 0.2, 0), power * 3, 1.0); - projectile.setTag(ARROW_FIRST_HIT, true); - } - }) - .filter(itemUpdateStateEvent -> itemUpdateStateEvent.getItemStack().material() == Material.BOW) - .build() - ); +// eventNode().addListener( TODO implement bow mechanism +// EventListener +// .builder(PlayerItemAnimationEvent.class) +// .handler(playerItemAnimationEvent -> playerItemAnimationEvent.getPlayer().setTag(CHARGE_BOW_SINCE, System.currentTimeMillis())) +// .filter(playerItemAnimationEvent -> playerItemAnimationEvent.getItemAnimationType() == PlayerItemAnimationEvent.ItemAnimationType.BOW) +// .build() +// ); +// +// eventNode().addListener( +// EventListener +// .builder(ItemUpdateStateEvent.class) +// .handler(event -> { +// final Player player = event.getPlayer(); +// final double chargedFor = (System.currentTimeMillis() - player.getTag(CHARGE_BOW_SINCE)) / 1000D; +// final double power = MathUtils.clamp((chargedFor * chargedFor + 2 * chargedFor) / 2D, 0, 1); +// +// if (power > 0.2) { +// final CustomEntityProjectile projectile = new CustomEntityProjectile(player, EntityType.ARROW); +// final ArrowMeta meta = (ArrowMeta) projectile.getEntityMeta(); +// meta.setCritical(power >= 0.9); +// projectile.scheduleRemove(Duration.of(100, TimeUnit.SERVER_TICK)); +// meta.setOnFire(true); +// +// final Pos position = player.getPosition().add(0, player.getEyeHeight(), 0); +// projectile.setInstance(Objects.requireNonNull(player.getInstance()), position); +// +// final Vec direction = projectile.getPosition().direction(); +// projectile.shootFrom(position.add(direction).sub(0, 0.2, 0), power * 3, 1.0); +// projectile.setTag(ARROW_FIRST_HIT, true); +// } +// }) +// .filter(itemUpdateStateEvent -> itemUpdateStateEvent.getItemStack().material() == Material.BOW) +// .build() +// ); eventNode().addListener( EventListener diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleefFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleefFactory.java index 86bc7f7..0466661 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleefFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/bowSpleef/BowSpleefFactory.java @@ -12,7 +12,12 @@ import java.util.Map; public class BowSpleefFactory implements GameFactory { @Override public TranslatedComponent name() { - return TranslatedComponent.byId(""); + return TranslatedComponent.byId("game_BowSpleef#name"); + } + + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_BowSpleef#description"); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/Deathcube.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/Deathcube.java index 73bcd03..4f0e7fb 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/Deathcube.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/Deathcube.java @@ -6,6 +6,7 @@ import eu.mhsl.minenet.minigames.util.BatchUtil; import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.world.BlockPallet; import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator; +import io.github.togar2.pvp.feature.CombatFeatures; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerMoveEvent; @@ -27,12 +28,13 @@ class Deathcube extends StatelessGame { this.percentage = percentage; this.setGenerator(new CircularPlateTerrainGenerator(radius+10).setPlateHeight(50)); -// if(pvpEnabled == 1) eventNode().addChild( // TODO update -// PvPConfig.emptyBuilder() -// .damage(DamageConfig.LEGACY.fallDamage(false)) -// .attack(AttackConfig.DEFAULT.attackCooldown(true)) -// .build().createNode() -// ); + if(pvpEnabled == 1) eventNode().addChild( + CombatFeatures.empty() + .add(CombatFeatures.VANILLA_ATTACK) + .add(CombatFeatures.VANILLA_DAMAGE) + .add(CombatFeatures.VANILLA_KNOCKBACK) + .build().createNode() + ); System.out.println(radius); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/DeathcubeFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/DeathcubeFactory.java index 8d16b2c..eda3151 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/DeathcubeFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/deathcube/DeathcubeFactory.java @@ -28,7 +28,7 @@ public class DeathcubeFactory implements GameFactory { .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30)) .addOption(new NumericOption("height", Material.SCAFFOLDING, TranslatedComponent.byId("optionCommon#height"), 10, 30, 50)) .addOption(new NumericOption("percentage", Material.COBWEB, TranslatedComponent.byId("game_Deathcube#optionPercentageBlocks"), 5, 7, 9, 11, 13)) - .addOption(new NumericOption("pvpEnabled", Material.STICK, TranslatedComponent.byId("game_Deathcube#optionPvpEnabled"), 1, 0)); + .addOption(new NumericOption("pvpEnabled", Material.STICK, TranslatedComponent.byId("game_Deathcube#optionPvpEnabled"), 0, 1)); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java index 2206789..27cd19f 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRace.java @@ -17,6 +17,7 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Vec; +import net.minestom.server.entity.EquipmentSlot; import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.player.*; @@ -39,6 +40,7 @@ public class ElytraRace extends StatelessGame { private final ValeGenerator vale = new ValeGenerator(); private final int gameHeight = 0; + private final int seaLevel = -55; private final int ringSpacing = 100; private final int ringCount; @@ -61,7 +63,7 @@ public class ElytraRace extends StatelessGame { this.ringCount = ringCount; setGenerator(vale); - vale.setCalculateSeaLevel(point -> -55); + vale.setCalculateSeaLevel(point -> seaLevel); vale.setXShiftMultiplier(integer -> NumberUtil.map(integer, 50, 500, 0, 1)); vale.addMixIn(new PlaneTerrainGenerator(gameHeight, Block.BARRIER)); @@ -105,9 +107,7 @@ public class ElytraRace extends StatelessGame { @Override protected void onLoad(@NotNull CompletableFuture callback) { Point spawnpoint = new Pos(vale.getXShiftAtZ(0), -46, 0); - GeneratorUtils.iterateArea(spawnpoint.sub(5, 0, 5), spawnpoint.add(5, 0, 5), point -> { - setBlock(point, BlockPallet.STREET.rnd()); - }); + GeneratorUtils.iterateArea(spawnpoint.sub(5, 0, 5), spawnpoint.add(5, 0, 5), point -> setBlock(point, BlockPallet.STREET.rnd())); generateRing(ringSpacing); generateRing(ringSpacing * 2); @@ -117,7 +117,7 @@ public class ElytraRace extends StatelessGame { @Override protected void onStart() { getPlayers().forEach(player -> { - player.getInventory().setChestplate(ItemStack.of(Material.ELYTRA)); + player.getInventory().setEquipment(EquipmentSlot.CHESTPLATE, (byte) 0, ItemStack.of(Material.ELYTRA)); for(int i = 0; i < 3; i++) { player.getInventory().setItemStack(i, ItemStack.builder(boostMaterial).customName(TranslatedComponent.byId("boost").getAssembled(player)).build()); } @@ -169,6 +169,7 @@ public class ElytraRace extends StatelessGame { if(newPos.z() > ringCount * ringSpacing) { getScore().insertResult(player); player.setGameMode(GameMode.SPECTATOR); + player.setFlyingWithElytra(false); } } @@ -194,8 +195,8 @@ public class ElytraRace extends StatelessGame { Point ringPos = getRingPositionAtZ(zPos); GeneratorUtils.iterateArea( - ringPos.sub(100, 0, 0).withY(0), // TODO 0 was before update getDimensionType().getMinY, might not work correctly - ringPos.add(100, 0, 0).withY(gameHeight), + ringPos.sub(100, 0, 0).withY(0), + ringPos.add(100, 0, 0).withY(seaLevel), point -> batch.setBlock(point, Block.BARRIER) ); GeneratorUtils.iterateArea( diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRaceFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRaceFactory.java index c805d47..2a61818 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRaceFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/elytraRace/ElytraRaceFactory.java @@ -14,7 +14,12 @@ import java.util.Map; public class ElytraRaceFactory implements GameFactory { @Override public TranslatedComponent name() { - return TranslatedComponent.byId(""); + return TranslatedComponent.byId("game_ElytraRace#name"); + } + + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_ElytraRace#description"); } @Override @@ -25,7 +30,7 @@ public class ElytraRaceFactory implements GameFactory { @Override public ConfigManager configuration() { return new ConfigManager() - .addOption(new NumericOption("ringCount", Material.DIAMOND_BLOCK, TranslatedComponent.byId("ringCount"), 5, 10, 20, 30, 40, 50)); + .addOption(new NumericOption("ringCount", Material.DIAMOND_BLOCK, TranslatedComponent.byId("game_ElytraRace#ringCount"), 5, 10, 20, 30, 40, 50)); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/jumpDive/JumpDive.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/jumpDive/JumpDive.java new file mode 100644 index 0000000..dd4cf4e --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/jumpDive/JumpDive.java @@ -0,0 +1,99 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.jumpDive; + +import eu.mhsl.minenet.minigames.instance.Dimension; +import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; +import eu.mhsl.minenet.minigames.score.PointsWinScore; +import eu.mhsl.minenet.minigames.util.BatchUtil; +import eu.mhsl.minenet.minigames.world.BlockPallet; +import net.kyori.adventure.sound.Sound; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.Player; +import net.minestom.server.event.player.PlayerMoveEvent; +import net.minestom.server.instance.batch.AbsoluteBlockBatch; +import net.minestom.server.instance.block.Block; +import net.minestom.server.sound.SoundEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.WeakHashMap; +import java.util.concurrent.CompletableFuture; + +public class JumpDive extends StatelessGame { + private final int radius; + private final int height; + private final int timeLimit; + + private final WeakHashMap scores = new WeakHashMap<>(); + + public JumpDive(int radius, int height, int timeLimit) { + super(Dimension.OVERWORLD.key, "jumpDive", new PointsWinScore()); + this.radius = radius; + this.height = height; + this.timeLimit = timeLimit; + } + + @Override + protected void onLoad(@NotNull CompletableFuture callback) { + AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); + + for(int x = -radius*2; x <= radius*2; x++) { + for(int z = -radius*2; z <= radius*2; z++) { + if(new Pos(x, 0, z).distance(new Pos(0, 0, 0)) > radius) { + batch.setBlock(x, height, z, BlockPallet.STONE.rnd()); + } else { + batch.setBlock(x, 0, z, BlockPallet.GROUND.rnd()); + batch.setBlock(x, 1, z, Block.WATER); + } + } + } + + BatchUtil.loadAndApplyBatch(batch, this, () -> callback.complete(null)); + } + + @Override + protected void onStart() { + setTimeLimit(timeLimit); + } + + @Override + protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { + Player p = playerMoveEvent.getPlayer(); + + if( + p.isOnGround() && playerMoveEvent.getNewPosition().y() < height + || playerMoveEvent.getNewPosition().y() < 0 + || isBeforeBeginning && playerMoveEvent.getNewPosition().y() < height + ) { + p.teleport(getSpawn()); + playerMoveEvent.setCancelled(true); + } + + if( + playerMoveEvent.getNewPosition().y() <= 1 + && playerMoveEvent.getNewPosition().distance(0, 1, 0) < radius + 0.5 + && !(!isBeforeBeginning && !isRunning) + ) { + setBlock(playerMoveEvent.getNewPosition().withY(1), Block.REDSTONE_BLOCK); + scores.merge(p, 1, Integer::sum); + p.teleport(getSpawn()); + playerMoveEvent.setCancelled(true); + p.playSound(Sound.sound(SoundEvent.ENTITY_EXPERIENCE_ORB_PICKUP, Sound.Source.PLAYER, 2f, 2f)); + } + } + + @Override + protected void onStop() { + getPlayers().forEach(player -> getScore().insertResult(player, scores.getOrDefault(player, 0))); + } + + @Override + public Pos getSpawn() { + double theta = rnd.nextDouble() * 2 * Math.PI; + + double spawnRadius = radius + 2; + double x = spawnRadius * Math.cos(theta); + double z = spawnRadius * Math.sin(theta); + + return new Pos(x, height + 2, z).withLookAt(new Pos(0, height, 0)); + + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/jumpDive/JumpDiveFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/jumpDive/JumpDiveFactory.java new file mode 100644 index 0000000..2e4fb2e --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/jumpDive/JumpDiveFactory.java @@ -0,0 +1,43 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.jumpDive; + +import eu.mhsl.minenet.minigames.instance.game.Game; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.ConfigManager; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.GameFactory; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.Option; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.common.NumericOption; +import eu.mhsl.minenet.minigames.instance.room.Room; +import eu.mhsl.minenet.minigames.message.component.TranslatedComponent; +import net.minestom.server.item.Material; + +import java.util.Map; + +public class JumpDiveFactory implements GameFactory { + @Override + public TranslatedComponent name() { + return TranslatedComponent.byId("game_jumpDive#name"); + } + + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_jumpDive#description"); + } + + @Override + public ConfigManager configuration() { + return new ConfigManager() + .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 5, 8, 10, 12, 14, 16)) + .addOption(new NumericOption("height", Material.SCAFFOLDING, TranslatedComponent.byId("optionCommon#height"), 30, 60, 90)) + .addOption(new NumericOption("timeLimit", Material.CLOCK, TranslatedComponent.byId("optionCommon#seconds"), 60, 120, 180, 240, 300)); + + } + + @Override + public Game manufacture(Room parent, Map> configuration) throws Exception { + return new JumpDive(configuration.get("radius").getAsInt(), configuration.get("height").getAsInt(), configuration.get("timeLimit").getAsInt()).setParent(parent); + } + + @Override + public Material symbol() { + return Material.WATER_BUCKET; + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/Minerun.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/Minerun.java index 3133046..79255b0 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/Minerun.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/Minerun.java @@ -10,7 +10,6 @@ import eu.mhsl.minenet.minigames.world.BlockPallet; import eu.mhsl.minenet.minigames.world.generator.terrain.SquarePlateTerrainGenerator; import net.kyori.adventure.sound.Sound; import net.minestom.server.coordinate.Pos; -import net.minestom.server.entity.Entity; import net.minestom.server.entity.GameMode; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerMoveEvent; @@ -87,7 +86,6 @@ class Minerun extends StatelessGame { if(Intersect.withPressurePlate(this, BlockPallet.PRESSURE_PLATES, middle)) { //Player died p.playSound(Sound.sound(SoundEvent.ENTITY_GENERIC_EXPLODE, Sound.Source.PLAYER, 1f, 1f)); - p.setPose(Entity.Pose.DYING); p.teleport(new Pos(p.getPosition().x(), getSpawn().y(), getSpawn().z())); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/MinerunFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/MinerunFactory.java index cb0d03a..ef534f5 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/MinerunFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/minerun/MinerunFactory.java @@ -22,7 +22,7 @@ public class MinerunFactory implements GameFactory { return new ConfigManager() .addOption(new NumericOption("width", Material.OAK_FENCE, TranslatedComponent.byId("optionCommon#width"), 10, 30, 50, 100)) .addOption(new NumericOption("length", Material.ZOMBIE_HEAD, TranslatedComponent.byId("optionCommon#length"), 50, 100, 150, 200)) - .addOption(new NumericOption("percentage", Material.LIGHT_WEIGHTED_PRESSURE_PLATE, TranslatedComponent.byId("game_Minerun#optionPercentageMiens"), 30, 40, 50, 60, 70)); + .addOption(new NumericOption("percentage", Material.LIGHT_WEIGHTED_PRESSURE_PLATE, TranslatedComponent.byId("game_Minerun#optionPercentageMines"), 30, 40, 50, 60, 70)); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/Spleef.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/Spleef.java index be73efd..d0b5969 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/Spleef.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/Spleef.java @@ -78,6 +78,7 @@ public class Spleef extends StatelessGame { } private void destroyBlock(PlayerStartDiggingEvent event) { + if(!isRunning) return; setBlock(event.getBlockPosition(), Block.AIR); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/SpleefFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/SpleefFactory.java index e238957..9d8a328 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/SpleefFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/spleef/SpleefFactory.java @@ -33,7 +33,7 @@ public class SpleefFactory implements GameFactory { @Override public ConfigManager configuration() { return new ConfigManager() - .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("game_Spleef#radius"), 10, 20, 30)) + .addOption(new NumericOption("radius", Material.HEART_OF_THE_SEA, TranslatedComponent.byId("optionCommon#radius"), 10, 20, 30)) .addOption(new NumericOption("stackCount", Material.SCAFFOLDING, TranslatedComponent.byId("game_Spleef#stackCount"), 1, 2, 3, 4, 5)); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java index 514562a..d3c6d02 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/stickfight/Stickfight.java @@ -3,29 +3,33 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.stickfight; import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.score.LastWinsScore; -import eu.mhsl.minenet.minigames.util.BatchUtil; import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator; -import io.github.togar2.pvp.config.PvPConfig; import io.github.togar2.pvp.events.FinalAttackEvent; +import io.github.togar2.pvp.feature.CombatFeatures; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.Player; import net.minestom.server.event.player.PlayerMoveEvent; -import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.block.Block; import org.jetbrains.annotations.NotNull; +import java.util.List; +import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; public class Stickfight extends StatelessGame { + private final double radius = 20; + private final WeakHashMap spawnPoints = new WeakHashMap<>(); + public Stickfight() { super(Dimension.OVERWORLD.key, "Stickfight", new LastWinsScore()); -// eventNode().addChild( // TODO update -// PvPConfig.emptyBuilder() -// .damage(DamageConfig.legacyBuilder().fallDamage(false)) -// .attack(AttackConfig.legacyBuilder().attackCooldown(true)) -// .build().createNode() -// ); + eventNode().addChild( + CombatFeatures.empty() + .add(CombatFeatures.VANILLA_ATTACK) + .add(CombatFeatures.VANILLA_DAMAGE) + .add(CombatFeatures.VANILLA_KNOCKBACK) + .build().createNode() + ); eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> { finalAttackEvent.setBaseDamage(0); @@ -37,21 +41,56 @@ public class Stickfight extends StatelessGame { @Override protected void onLoad(@NotNull CompletableFuture callback) { - AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); - for (int z = -10; z <= 10; z++) { - batch.setBlock(0, 50, z, Block.SANDSTONE); - } - batch.setBlock(0, 50, 0, Block.GOLD_BLOCK); + setBlock(0, 50, 0, Block.DIAMOND_BLOCK); + } - BatchUtil.loadAndApplyBatch(batch, this, () -> callback.complete(null)); + @Override + protected void start() { + List players = getPlayers().stream().toList(); + int numPlayers = players.size(); + + for (int i = 0; i < numPlayers; i++) { + double angle = (2 * Math.PI / numPlayers) * i; + int spawnX = (int) (radius * Math.cos(angle)); + int spawnZ = (int) (radius * Math.sin(angle)); + int spawnY = 50; + + Pos spawnpoint = new Pos(spawnX, spawnY + 1, spawnZ).add(0.5); + spawnPoints.put(players.get(i), spawnpoint.withLookAt(getSpawn())); + players.get(i).teleport(spawnpoint); + + generateBridge(spawnX, spawnY, spawnZ); + } + + setBlock(0, 50, 0, Block.GOLD_BLOCK); + super.start(); + } + + private void generateBridge(int startX, int startY, int startZ) { + int steps = (int) (radius * 1.5); + for (int i = 0; i < steps; i++) { + double t = (double) i / steps; + int x = (int) (startX * (1 - t)); + int z = (int) (startZ * (1 - t)); + setBlock(x, startY, z, Block.SANDSTONE); + } } @Override protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { - if(isBeforeBeginning) playerMoveEvent.setCancelled(true); + if(!spawnPoints.containsKey(playerMoveEvent.getPlayer())) { + playerMoveEvent.setCancelled(true); + return; + } + + if(isBeforeBeginning) { + if(spawnPoints.get(playerMoveEvent.getPlayer()).distance(playerMoveEvent.getNewPosition()) < 1) return; + playerMoveEvent.setCancelled(true); + playerMoveEvent.getPlayer().teleport(spawnPoints.get(playerMoveEvent.getPlayer())); + } if(playerMoveEvent.getNewPosition().y() < 40) { - playerMoveEvent.getPlayer().teleport(new Pos(0, 51, 0)); + playerMoveEvent.getPlayer().teleport(spawnPoints.get(playerMoveEvent.getPlayer())); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/Tetris.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/Tetris.java new file mode 100644 index 0000000..0107b50 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/Tetris.java @@ -0,0 +1,160 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris; + +import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; +import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game.TetrisGame; +import eu.mhsl.minenet.minigames.instance.Dimension; +import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game.Tetromino; +import eu.mhsl.minenet.minigames.score.PointsWinScore; +import net.kyori.adventure.text.Component; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.GameMode; +import net.minestom.server.entity.Player; +import net.minestom.server.event.player.*; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +class Tetris extends StatelessGame { + private final Map tetrisGames = new WeakHashMap<>(); + private final int nextTetrominoesCount; + private final boolean isFast; + private final boolean hasCombat; + private boolean setTimeLimit = false; + private final long randomSeed; + + public Tetris(int nextTetrominoesCount, boolean isFast, boolean hasCombat) { + super(Dimension.THE_END.key, "Tetris", new PointsWinScore()); + + this.eventNode() + .addListener(PlayerUseItemEvent.class, this::onPlayerInteract) + .addListener(PlayerHandAnimationEvent.class, this::onPlayerAttack) + .addListener(PlayerTickEvent.class, this::onPlayerTick); + + this.nextTetrominoesCount = nextTetrominoesCount; + this.isFast = isFast; + this.hasCombat = hasCombat; + + Random random = new Random(); + this.randomSeed = random.nextLong(); + } + + @Override + protected void onStart() { + this.getEntities().stream() + .filter(entity -> entity.getEntityType().equals(Tetromino.getGhostEntityType())) + .forEach(Entity::remove); + + if(this.hasCombat) { + this.tetrisGames.values().forEach(tetrisGame -> tetrisGame.updateOtherTetrisGames(this.tetrisGames.values())); + } + + this.tetrisGames.forEach((player, tetrisGame) -> tetrisGame.start()); + } + + @Override + protected void onStop() { + this.tetrisGames.forEach((player, tetrisGame) -> { + tetrisGame.loose(); + this.getScore().insertResult(player, tetrisGame.getScore()); + tetrisGame.sidebar.removeViewer(player); + }); + } + + @Override + protected void onPlayerLeave(Player p) { + this.tetrisGames.get(p).sidebar.removeViewer(p); + this.letPlayerLoose(p); + } + + @Override + protected void onPlayerMove(@NotNull PlayerMoveEvent event) { + Player player = event.getPlayer(); + Pos currentPosition = event.getNewPosition(); + + TetrisGame tetrisGame = this.tetrisGames.get(player); + + if(tetrisGame == null) { + event.setCancelled(true); + return; + } + if(tetrisGame.lost) return; + if(player.getGameMode() == GameMode.SPECTATOR) return; + + if(player.inputs().forward()) tetrisGame.pressedButton(TetrisGame.Button.W); + if(player.inputs().backward()) tetrisGame.pressedButton(TetrisGame.Button.S); + if(player.inputs().right()) tetrisGame.pressedButton(TetrisGame.Button.D); + if(player.inputs().left()) tetrisGame.pressedButton(TetrisGame.Button.A); + if(player.inputs().jump()) tetrisGame.pressedButton(TetrisGame.Button.space); + + event.setNewPosition(tetrisGame.getPlayerSpawnPosition().withView(currentPosition)); + player.setSprinting(false); + } + + protected void onPlayerInteract(@NotNull PlayerUseItemEvent event) { + this.tetrisGames.get(event.getPlayer()).pressedButton(TetrisGame.Button.mouseRight); + } + + protected void onPlayerAttack(@NotNull PlayerHandAnimationEvent event) { + this.tetrisGames.get(event.getPlayer()).pressedButton(TetrisGame.Button.mouseLeft); + } + + protected void onPlayerTick(PlayerTickEvent event) { + Player player = event.getPlayer(); + TetrisGame tetrisGame = this.tetrisGames.get(player); + if(tetrisGame == null) return; + if(tetrisGame.lost && player.getGameMode() != GameMode.SPECTATOR) { + this.letPlayerLoose(player); + } + } + + private void letPlayerLoose(Player player) { + TetrisGame tetrisGame = this.tetrisGames.get(player); + player.setGameMode(GameMode.SPECTATOR); + player.setInvisible(true); + this.getScore().insertResult(player, tetrisGame.getScore()); + + boolean allGamesLost = this.tetrisGames.values().stream() + .filter(game -> !game.lost) + .toList() + .isEmpty(); + if(!this.setTimeLimit && !allGamesLost) { + this.setTimeLimit(90); + this.setTimeLimit = true; + } + } + + @Override + protected boolean onPlayerJoin(Player p) { + p.getInventory().setItemStack(0, ItemStack.builder(Material.BIRCH_BUTTON).customName(Component.text("Controller")).build()); + p.setSprinting(false); + + if(this.tetrisGames.get(p) == null) { + this.tetrisGames.put(p, new TetrisGame( + this, + this.getSpawn().sub(6, 8, 15).add(this.tetrisGames.size()*30, 0, 0), + Tetromino.Shape.J, + this.nextTetrominoesCount, + this.isFast, + this.hasCombat, + this.randomSeed + )); + this.tetrisGames.get(p).generate(); + this.tetrisGames.values().forEach(tetrisGame -> tetrisGame.updateOtherTetrisGames(this.tetrisGames.values())); + } + + TetrisGame tetrisGame = this.tetrisGames.get(p); + + p.teleport(tetrisGame.getPlayerSpawnPosition()); + tetrisGame.sidebar.addViewer(p); + + return super.onPlayerJoin(p); + } + + @Override + public Pos getSpawn() { + return new Pos(0, 30, 15).withView(180, 0); + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/TetrisFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/TetrisFactory.java new file mode 100644 index 0000000..56630f7 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/TetrisFactory.java @@ -0,0 +1,43 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris; + +import eu.mhsl.minenet.minigames.instance.game.Game; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.GameFactory; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.Option; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.ConfigManager; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.common.BoolOption; +import eu.mhsl.minenet.minigames.instance.game.stateless.config.common.NumericOption; +import eu.mhsl.minenet.minigames.instance.room.Room; +import eu.mhsl.minenet.minigames.message.component.TranslatedComponent; +import net.minestom.server.item.Material; + +import java.util.Map; + +public class TetrisFactory implements GameFactory { + @Override + public TranslatedComponent name() { + return TranslatedComponent.byId("game_Tetris#name"); + } + + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_Tetris#description"); + } + + @Override + public ConfigManager configuration() { + return new ConfigManager() + .addOption(new NumericOption("nextTetrominoesCount", Material.LADDER, TranslatedComponent.byId("game_Tetris#nextTetrominoesCount"), 3, 4, 5, 1, 2)) + .addOption(new BoolOption("isFast", Material.MINECART, TranslatedComponent.byId("game_Tetris#isFast"))) + .addOption(new BoolOption("hasCombat", Material.DIAMOND_SWORD, TranslatedComponent.byId("game_Tetris#hasCombat"))); + } + + @Override + public Game manufacture(Room parent, Map> configuration) { + return new Tetris(configuration.get("nextTetrominoesCount").getAsInt(), configuration.get("isFast").getAsBoolean(), configuration.get("hasCombat").getAsBoolean()).setParent(parent); + } + + @Override + public Material symbol() { + return Material.PURPLE_WOOL; + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java new file mode 100644 index 0000000..115e0fa --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Playfield.java @@ -0,0 +1,191 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game; + +import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; +import eu.mhsl.minenet.minigames.util.BatchUtil; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.instance.batch.AbsoluteBlockBatch; +import net.minestom.server.instance.block.Block; +import org.apache.commons.lang3.ArrayUtils; + +import java.util.Random; + +public class Playfield { + private final Pos lowerLeftCorner; + private final StatelessGame instance; + private final static int height = 22; + private final static Block scoreBlock = Block.STONE; + private final int nextTetrominoesCount; + private final Random random; + + public Playfield(Pos lowerLeftCorner, StatelessGame instance, int nextTetrominoesCount) { + this.nextTetrominoesCount = nextTetrominoesCount; + this.lowerLeftCorner = lowerLeftCorner; + this.instance = instance; + this.random = new Random(); + } + + public Pos getPlayerSpawnPosition() { + return this.lowerLeftCorner.add(6, 9, 20).withView(180, 0); + } + + public Pos getTetrominoSpawnPosition() { + return this.lowerLeftCorner.add(5, 21, 1); + } + + public Pos getHoldPosition() { + return this.lowerLeftCorner.add(-4, 18, 1); + } + + public Pos getNextPosition() { + return this.lowerLeftCorner.add(15, 18, 1); + } + + public Pos getScorePosition() { + return this.lowerLeftCorner.add(-5, height+3, 0); + } + + public void generate() { + AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); + + // actual playfield: + for(int x=0; x<12; x++) { + for(int y = 0; y< height; y++) { + batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GLASS); + batch.setBlock(this.lowerLeftCorner.add(x, y, -1), Block.BLACK_CONCRETE); + + if(x==0 || x==11 || y==0) { + batch.setBlock(this.lowerLeftCorner.add(x, y, 1), Block.GRAY_CONCRETE); + batch.setBlock(this.lowerLeftCorner.add(x, y, 0), Block.GRAY_CONCRETE); + } + } + } + + // hold position: + for(int x = 0; x < 4; x++) { + for(int y = 0; y < 4; y++) { + batch.setBlock(this.getHoldPosition().add(x-1, y-1, -1), Block.QUARTZ_BLOCK); + } + } + + // next positions: + for(int x = 0; x < 4; x++) { + for(int y = -4*this.nextTetrominoesCount+4; y < 4; y++) { + batch.setBlock(this.getNextPosition().add(x-1, y-1, -1), Block.QUARTZ_BLOCK); + } + } + + batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 0), Block.STONE); + batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 0), Block.STONE); + batch.setBlock(this.getPlayerSpawnPosition().sub(1, 1, 1), Block.STONE); + batch.setBlock(this.getPlayerSpawnPosition().sub(0, 1, 1), Block.STONE); + + BatchUtil.loadAndApplyBatch(batch, this.instance, () -> {}); + } + + public int removeFullLines() { + int removedLinesCounter = 0; + for(int y = 1; y< height; y++) { + boolean isFullLine = true; + for(int x=1; x<11; x++) { + if(this.instance.getBlock(this.lowerLeftCorner.add(x, y, 1)) == Block.AIR) isFullLine = false; + } + if(isFullLine) { + this.removeFullLine(y); + removedLinesCounter += 1; + y -= 1; + } + } + return removedLinesCounter; + } + + public void addLines(int lines) { + int xPosMissing = this.random.nextInt(1, 10); + + for (int i = 0; i < lines; i++) { + this.moveAllLinesUp(); + for (int x = 1; x < 11; x++) { + if(x != xPosMissing) { + this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.LIGHT_GRAY_CONCRETE); + } else { + this.instance.setBlock(this.lowerLeftCorner.add(x, 1, 1), Block.AIR); + } + } + } + } + + public void updateAttackingLines(int attackingLines) { + for (int y = 0; y < height + 5; y++) { + if(attackingLines > 0) { + this.instance.setBlock(this.lowerLeftCorner.add(12, y, 1), Block.REDSTONE_BLOCK); + attackingLines -= 1; + } else { + this.instance.setBlock(this.lowerLeftCorner.add(12, y, 1), Block.AIR); + } + } + } + + public void updateScore(int score) { + this.removeDigits(); + String scoreString = String.valueOf(score); + char[] characters = scoreString.toCharArray(); + ArrayUtils.reverse(characters); + for(int i = 6; i > 0; i--) { + char digit; + if(i <= characters.length) { + digit = characters[i-1]; + } else { + digit = '0'; + } + this.displayDigit(digit, 6-i); + } + } + + + private void displayDigit(char digit, int positionFromLeft) { + int[][] digitArray; + switch (digit) { + case '1' -> digitArray = new int[][]{{0,0,1},{0,1,1},{0,0,1},{0,0,1},{0,0,1}}; + case '2' -> digitArray = new int[][]{{1,1,1},{0,0,1},{1,1,1},{1,0,0},{1,1,1}}; + case '3' -> digitArray = new int[][]{{1,1,1},{0,0,1},{0,1,1},{0,0,1},{1,1,1}}; + case '4' -> digitArray = new int[][]{{1,0,1},{1,0,1},{1,1,1},{0,0,1},{0,0,1}}; + case '5' -> digitArray = new int[][]{{1,1,1},{1,0,0},{1,1,1},{0,0,1},{1,1,1}}; + case '6' -> digitArray = new int[][]{{1,1,1},{1,0,0},{1,1,1},{1,0,1},{1,1,1}}; + case '7' -> digitArray = new int[][]{{1,1,1},{0,0,1},{0,1,0},{0,1,0},{0,1,0}}; + case '8' -> digitArray = new int[][]{{1,1,1},{1,0,1},{1,1,1},{1,0,1},{1,1,1}}; + case '9' -> digitArray = new int[][]{{1,1,1},{1,0,1},{1,1,1},{0,0,1},{1,1,1}}; + default -> digitArray = new int[][]{{1,1,1},{1,0,1},{1,0,1},{1,0,1},{1,1,1}}; + } + + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 5; y++) { + if(digitArray[4-y][x] == 1) this.instance.setBlock(this.getScorePosition().add(positionFromLeft*4+x, y, 0), scoreBlock); + } + } + } + + private void removeDigits() { + for (int x = 0; x < 4 * 6; x++) { + for (int y = 0; y < 5; y++) { + this.instance.setBlock(this.getScorePosition().add(x, y, 0), Block.AIR); + } + } + } + + private void moveAllLinesUp() { + for (int y = height + 3; y > 1; y--) { + for (int x = 1; x < 11; x++) { + Block blockBeneath = this.instance.getBlock(this.lowerLeftCorner.add(x, y - 1, 1)); + this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockBeneath); + } + } + } + + private void removeFullLine(int positionY) { + for(int y = positionY; y< height; y++) { + for(int x=1; x<11; x++) { + Block blockAbove = this.instance.getBlock(this.lowerLeftCorner.add(x, y+1, 1)); + this.instance.setBlock(this.lowerLeftCorner.add(x, y, 1), blockAbove); + } + } + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java new file mode 100644 index 0000000..dc6cabe --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/TetrisGame.java @@ -0,0 +1,318 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game; + +import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; +import net.kyori.adventure.text.Component; +import net.minestom.server.MinecraftServer; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.scoreboard.Sidebar; +import net.minestom.server.timer.Scheduler; +import net.minestom.server.timer.TaskSchedule; + +import java.util.*; + +public class TetrisGame { + private final StatelessGame instance; + private final Playfield playfield; + private final boolean isFast; + private int level = 1; + private int lines = 0; + private int score = 0; + private int combo = 0; + private int attackingLines = 0; + public boolean lost = false; + public boolean paused = true; + private final boolean hasCombat; + public Tetromino currentTetromino; + private final List nextTetrominoes = new ArrayList<>(); + private Tetromino holdTetromino; + private final List tetrominoBag = new ArrayList<>(); + private boolean holdPossible = true; + private final Pos nextPosition; + private final Pos holdPosition; + private final Pos tetrominoSpawnPosition; + public Sidebar sidebar = new Sidebar(Component.text("Info:")); + private final Map lastPresses = new HashMap<>(); + private final List otherTetrisGames = new ArrayList<>(); + private final Random random; + + public enum Button { + W, + A, + S, + D, + mouseLeft, + mouseRight, + space + } + + public TetrisGame(StatelessGame instance, Pos lowerLeftCorner, Tetromino.Shape startTetrominoShape, int nextTetrominoesCount, boolean isfast, boolean hasCombat, long randomSeed) { + this.isFast = isfast; + this.hasCombat = hasCombat; + this.instance = instance; + this.playfield = new Playfield(lowerLeftCorner, this.instance, nextTetrominoesCount); + this.random = new Random(randomSeed); + + this.holdPosition = this.playfield.getHoldPosition(); + this.nextPosition = this.playfield.getNextPosition(); + this.tetrominoSpawnPosition = this.playfield.getTetrominoSpawnPosition(); + this.buildSidebar(); + + this.currentTetromino = new Tetromino(this.instance, startTetrominoShape); + for (int i = 0; i < nextTetrominoesCount; i++) { + this.updateNextTetrominoes(); + } + } + + public void pressedButton(Button button) { + final int standardButtonDelay = 100; + final int buttonDebounce = 70; + + if(this.lastPresses.getOrDefault(button, 0L) >= System.currentTimeMillis()-standardButtonDelay) return; + + this.lastPresses.put(button, System.currentTimeMillis()); + if(button == Button.W) this.lastPresses.put(button, System.currentTimeMillis()+buttonDebounce); + if(button == Button.S) this.lastPresses.put(button, System.currentTimeMillis()-buttonDebounce); + + if(this.lost || this.paused) return; + + switch (button) { + case A -> this.currentTetromino.moveLeft(); + case S -> this.moveDown(); + case D -> this.currentTetromino.moveRight(); + case W -> this.hardDrop(); + case mouseLeft -> this.currentTetromino.rotate(false); + case mouseRight -> this.currentTetromino.rotate(true); + case space -> this.switchHold(); + } + } + + public Pos getPlayerSpawnPosition() { + return this.playfield.getPlayerSpawnPosition(); + } + + public void start() { + this.paused = false; + Scheduler scheduler = MinecraftServer.getSchedulerManager(); + scheduler.submitTask(() -> { + if(this.lost) return TaskSchedule.stop(); + int standardTickDelay = 40; + if(this.isFast) standardTickDelay = 20; + + TaskSchedule nextTick = TaskSchedule.tick(Math.round((float) standardTickDelay /this.level)); + if(this.paused) return nextTick; + this.tick(); + return nextTick; + }); + + this.updateInfo(); + this.nextTetrominoes.forEach(tetromino -> { + double xChange = -tetromino.getXChange(); + tetromino.setPosition(this.nextPosition.sub(xChange, 4*this.nextTetrominoes.indexOf(tetromino), 0)); + tetromino.drawAsEntities(); + }); + } + + public void generate() { + this.playfield.generate(); + + this.currentTetromino.setPosition(this.tetrominoSpawnPosition); + this.currentTetromino.draw(false); + } + + public void tick() { + if(this.lost || this.paused) return; + if(!this.currentTetromino.moveDown()) { + this.setActiveTetrominoDown(); + } + } + + public int getScore() { + return this.score; + } + + public void updateOtherTetrisGames(Collection tetrisGames) { + List games = new ArrayList<>(tetrisGames); + games.remove(this); + games.removeIf(tetrisGame -> tetrisGame.lost); + this.otherTetrisGames.clear(); + this.otherTetrisGames.addAll(games); + } + + public void getAttacked(int lines) { + if(this.hasCombat && !this.lost) { + this.attackingLines += lines; + this.playfield.updateAttackingLines(this.attackingLines); + } + } + + public void loose() { + this.lost = true; + } + + + private boolean moveDown() { + if(!this.currentTetromino.moveDown()) { + this.setActiveTetrominoDown(); + return false; + } + this.score += 1; + this.updateInfo(); + return true; + } + + private boolean hardDrop() { + if(!this.currentTetromino.moveDown()) { + this.setActiveTetrominoDown(); + return false; + } + this.score += 2; + this.updateInfo(); + while(this.currentTetromino.moveDown()) { + this.score += 2; + this.updateInfo(); + } + this.setActiveTetrominoDown(); + return true; + } + + private boolean switchHold() { + if(!this.holdPossible) return false; + + Tetromino newCurrentTetromino; + if(this.holdTetromino == null) { + newCurrentTetromino = this.nextTetrominoes.removeFirst(); + newCurrentTetromino.remove(); + this.updateNextTetrominoes(); + } else { + newCurrentTetromino = this.holdTetromino; + this.holdTetromino.remove(); + } + + this.currentTetromino.remove(); + this.holdTetromino = new Tetromino(this.instance, this.currentTetromino.getShape()); + this.currentTetromino = newCurrentTetromino; + + this.currentTetromino.setPosition(this.tetrominoSpawnPosition); + this.currentTetromino.draw(); + if(!this.currentTetromino.moveDown()) this.loose(); + + double xChange = this.holdTetromino.getXChange(); + this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0)); + this.holdTetromino.drawAsEntities(); + this.holdPossible = false; + return true; + } + + + private void updateNextTetrominoes() { + if(this.tetrominoBag.isEmpty()) { + for(Tetromino.Shape shape : Tetromino.Shape.values()) { + this.tetrominoBag.add(new Tetromino(this.instance, shape)); + } + Collections.shuffle(this.tetrominoBag, this.random); + } + + if(!this.nextTetrominoes.isEmpty()) this.nextTetrominoes.forEach(Tetromino::remove); + Tetromino newTetromino = this.tetrominoBag.removeFirst(); + this.nextTetrominoes.add(newTetromino); + this.nextTetrominoes.forEach(tetromino -> { + double xChange = -tetromino.getXChange(); + tetromino.setPosition(this.nextPosition.sub(xChange, 4*this.nextTetrominoes.indexOf(tetromino), 0)); + tetromino.drawAsEntities(); + }); + } + + private void buildSidebar() { + this.sidebar.createLine(new Sidebar.ScoreboardLine( + "0", + Component.text("Score: "), + 0 + )); + this.sidebar.createLine(new Sidebar.ScoreboardLine( + "1", + Component.text("Lines: "), + 0 + )); + this.sidebar.createLine(new Sidebar.ScoreboardLine( + "2", + Component.text("Level: "), + 1 + )); + } + + private void updateInfo() { + this.playfield.updateScore(this.score); + this.sidebar.updateLineScore("0", this.score); + this.sidebar.updateLineScore("1", this.lines); + this.sidebar.updateLineScore("2", this.level); + } + + private void setActiveTetrominoDown() { + this.currentTetromino.removeOwnEntities(); + this.currentTetromino = this.nextTetrominoes.removeFirst(); + this.currentTetromino.remove(); + this.updateNextTetrominoes(); + + int removedLines = this.playfield.removeFullLines(); + int combatLines = 0; + this.combo += 1; + switch (removedLines) { + case 0 -> this.combo = 0; + case 1 -> { + this.lines += 1; + this.score += 40 * this.level; + } + case 2 -> { + combatLines = 1; + this.lines += 2; + this.score += 100 * this.level; + } + case 3 -> { + combatLines = 2; + this.lines += 3; + this.score += 300 * this.level; + } + case 4 -> { + combatLines = 4; + this.lines += 4; + this.score += 1200 * this.level; + } + } + + this.score += 50 * this.combo * this.level; + if(this.combo >= 2) { + combatLines += (int) Math.floor((double) this.combo /2); + } + + if(this.hasCombat && this.attackingLines > 0) { + if(combatLines > 0 && this.attackingLines >= combatLines) { + this.attackingLines -= combatLines; + combatLines = 0; + } else if(combatLines > 0) { + combatLines -= this.attackingLines; + this.attackingLines = 0; + } else { + this.playfield.addLines(this.attackingLines); + this.attackingLines = 0; + } + + this.playfield.updateAttackingLines(this.attackingLines); + } + + if(this.hasCombat && !this.otherTetrisGames.isEmpty()) { + Collections.shuffle(this.otherTetrisGames); + this.otherTetrisGames.getFirst().getAttacked(combatLines); + } + + this.level = (int) Math.floor((double) this.lines / 10) + 1; + this.holdPossible = true; + + this.updateInfo(); + + this.currentTetromino.setPosition(this.tetrominoSpawnPosition); + this.currentTetromino.draw(); + if(!this.currentTetromino.moveDown()) { + this.loose(); + } + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java new file mode 100644 index 0000000..1c8a95c --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tetris/game/Tetromino.java @@ -0,0 +1,246 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game; + +import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.Entity; +import net.minestom.server.entity.EntityType; +import net.minestom.server.entity.metadata.other.FallingBlockMeta; +import net.minestom.server.instance.block.Block; +import net.minestom.server.tag.Tag; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; + +public class Tetromino { + private final Shape shape; + private final StatelessGame instance; + private Pos position; + private int[][] shapeArray; + private final static EntityType ghostEntityType = EntityType.FALLING_BLOCK; + private final UUID uuid; + private final static Tag uuidTag = Tag.String("uuid"); + + public enum Shape { + I, + J, + L, + O, + S, + T, + Z + } + + public Tetromino(StatelessGame instance, Shape shape) { + this.instance = instance; + this.shape = shape; + this.uuid = UUID.randomUUID(); + + switch (this.shape) { + case I -> this.shapeArray = new int[][]{{0, 0, 0, 0}, {1, 1, 1, 1}, {0, 0, 0, 0}, {0, 0, 0, 0}}; + case J -> this.shapeArray = new int[][]{{1,0,0}, {1,1,1}, {0,0,0}}; + case L -> this.shapeArray = new int[][]{{0,0,1}, {1,1,1}, {0,0,0}}; + case O -> this.shapeArray = new int[][]{{1,1}, {1,1}}; + case S -> this.shapeArray = new int[][]{{0,1,1}, {1,1,0}, {0,0,0}}; + case T -> this.shapeArray = new int[][]{{0,1,0}, {1,1,1}, {0,0,0}}; + case Z -> this.shapeArray = new int[][]{{1,1,0}, {0,1,1}, {0,0,0}}; + } + } + + public static EntityType getGhostEntityType() { + return ghostEntityType; + } + + public void setPosition(Pos newPosition) { + this.position = newPosition; + } + + public boolean rotate(boolean clockwise) { + int[][] newShapeArray = this.getTurnedShapeArray(clockwise); + return this.checkCollisionAndMove(this.position, newShapeArray); + } + + public boolean moveDown() { + Pos newPosition = this.position.sub(0, 1, 0); + return this.checkCollisionAndMove(newPosition, this.shapeArray); + } + + public boolean moveLeft() { + Pos newPosition = this.position.sub(1, 0, 0); + return this.checkCollisionAndMove(newPosition, this.shapeArray); + } + + public boolean moveRight() { + Pos newPosition = this.position.add(1, 0, 0); + return this.checkCollisionAndMove(newPosition, this.shapeArray); + } + + public void draw() { + this.draw(true); + } + + public void draw(boolean withGhost) { + if(withGhost) { + Pos ghostPos = this.position; + while (!this.checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) { + ghostPos = ghostPos.sub(0, 1, 0); + } + Pos positionChange = this.position.sub(ghostPos); + this.getBlockPositions().forEach(pos -> { + Entity ghostBlock = new Entity(ghostEntityType); + ((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getGhostBlock()); + ghostBlock.setNoGravity(true); + ghostBlock.setGlowing(true); + ghostBlock.setTag(uuidTag, this.uuid.toString()); + ghostBlock.setInstance(this.instance, pos.sub(positionChange).add(0.5, 0, 0.5)); + }); + } + + this.getBlockPositions().forEach(pos -> this.instance.setBlock(pos, this.getColoredBlock())); + } + + public void drawAsEntities() { + this.getBlockPositions().forEach(pos -> { + Entity ghostBlock = new Entity(ghostEntityType); + ((FallingBlockMeta) ghostBlock.getEntityMeta()).setBlock(this.getColoredBlock()); + ghostBlock.setNoGravity(true); + ghostBlock.setTag(uuidTag, this.uuid.toString()); + ghostBlock.setInstance(this.instance, pos.add(0.5, 0, 0.5)); + }); + } + + public void remove() { + this.removeOwnEntities(); + this.getBlockPositions().forEach(pos -> this.instance.setBlock(pos, Block.AIR)); + } + + public Block getColoredBlock() { + Block returnBlock; + switch (this.shape) { + case I -> returnBlock = Block.LIGHT_BLUE_CONCRETE; + case J -> returnBlock = Block.BLUE_CONCRETE; + case L -> returnBlock = Block.ORANGE_CONCRETE; + case O -> returnBlock = Block.YELLOW_CONCRETE; + case S -> returnBlock = Block.GREEN_CONCRETE; + case T -> returnBlock = Block.PURPLE_CONCRETE; + case Z -> returnBlock = Block.RED_CONCRETE; + default -> returnBlock = Block.WHITE_CONCRETE; + } + return returnBlock; + } + + public void removeOwnEntities() { + this.instance.getEntities().stream() + .filter(entity -> { + String tagValue = entity.getTag(uuidTag); + if(tagValue == null) return false; + return entity.getTag(uuidTag).equals(this.uuid.toString()); + }) + .forEach(Entity::remove); + } + + public double getXChange() { + switch (this.shape) { + case O, I -> { + return 0; + } + case null, default -> { + return 0.5; + } + } + } + + + public Shape getShape() { + return this.shape; + } + + private Block getGhostBlock() { + Block returnBlock; + switch (this.shape) { + case I -> returnBlock = Block.LIGHT_BLUE_STAINED_GLASS; + case J -> returnBlock = Block.BLUE_STAINED_GLASS; + case L -> returnBlock = Block.ORANGE_STAINED_GLASS; + case O -> returnBlock = Block.YELLOW_STAINED_GLASS; + case S -> returnBlock = Block.GREEN_STAINED_GLASS; + case T -> returnBlock = Block.PURPLE_STAINED_GLASS; + case Z -> returnBlock = Block.RED_STAINED_GLASS; + default -> returnBlock = Block.WHITE_STAINED_GLASS; + } + return returnBlock; + } + + private int[][] getTurnedShapeArray(boolean clockwise) { + int iterations = 1; + if(!clockwise) iterations = 3; + + int arrayLength = this.shapeArray.length; + int[][] startArray = Arrays.stream(this.shapeArray).map(int[]::clone).toArray(int[][]::new); + int[][] returnArray = new int[arrayLength][arrayLength]; + + for(int k=0; k pos.equals(position)); + } + + private List getBlockPositions() { + return this.getBlockPositions(this.position, this.shapeArray); + } + + private List getBlockPositions(Pos position, int[][] shapeArray) { + List returnList = new ArrayList<>(); + if(position == null) return returnList; + + int arrayLength = shapeArray.length; + + for(int x=0; x returnList.add(position.add(x-1, y-2, 0)); + case O -> returnList.add(position.add(x, y, 0)); + default -> returnList.add(position.add(x-1, y-1, 0)); + } + } + } + } + + return returnList; + } + + private boolean checkCollision(Pos newPosition, int[][] newShapeArray) { + List newBlockPositions = this.getBlockPositions(newPosition, newShapeArray); + + for(Pos pos : newBlockPositions) { + if(this.isPartOfTetromino(pos)) continue; + if(this.instance.getBlock(pos) == this.getGhostBlock()) continue; + if(this.instance.getBlock(pos) != Block.AIR) return true; + } + + return false; + } + + private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) { + if(!this.checkCollision(newPosition, newShapeArray)) { + this.remove(); + this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new); + this.setPosition(newPosition); + this.draw(); + return true; + } + return false; + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRun.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRun.java index db962ec..403a657 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRun.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRun.java @@ -7,10 +7,7 @@ import eu.mhsl.minenet.minigames.util.BatchUtil; import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator; import net.minestom.server.MinecraftServer; import net.minestom.server.coordinate.Pos; -import net.minestom.server.entity.Entity; -import net.minestom.server.entity.EntityType; import net.minestom.server.entity.GameMode; -import net.minestom.server.entity.metadata.other.PrimedTntMeta; import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.block.Block; @@ -51,7 +48,13 @@ public class TntRun extends StatelessGame { @Override protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) { + + if(playerMoveEvent.getNewPosition().y() < totalElevation) { + if(isBeforeBeginning) { + playerMoveEvent.getPlayer().teleport(getSpawn()); + return; + } playerMoveEvent.getPlayer().setGameMode(GameMode.SPECTATOR); getScore().insertResult(playerMoveEvent.getPlayer()); } @@ -67,10 +70,10 @@ public class TntRun extends StatelessGame { setBlock(firstLocation, Block.AIR); setBlock(secondLocation, Block.AIR); - Entity fallingTnt = new Entity(EntityType.TNT); - PrimedTntMeta fallingTntMeta = (PrimedTntMeta) fallingTnt.getEntityMeta(); - fallingTntMeta.setFuseTime(20); - fallingTnt.setInstance(this, secondLocation); +// Entity fallingTnt = new Entity(EntityType.TNT); +// PrimedTntMeta fallingTntMeta = (PrimedTntMeta) fallingTnt.getEntityMeta(); +// fallingTntMeta.setFuseTime(20); +// fallingTnt.setInstance(this, secondLocation); } } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRunFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRunFactory.java index 0bc3dd4..79665b2 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRunFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/tntrun/TntRunFactory.java @@ -14,7 +14,12 @@ import java.util.Map; public class TntRunFactory implements GameFactory { @Override public TranslatedComponent name() { - return TranslatedComponent.byId("game_tntRun#name"); + return TranslatedComponent.byId("game_TntRun#name"); + } + + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_TntRun#description"); } @Override @@ -25,8 +30,8 @@ public class TntRunFactory implements GameFactory { @Override public ConfigManager configuration() { return new ConfigManager() - .addOption(new NumericOption("radius", Material.STICK, TranslatedComponent.byId("game_tntRun#radius"), 20, 30, 50, 60)) - .addOption(new NumericOption("levels", Material.SCAFFOLDING, TranslatedComponent.byId("game_tntRun#levels"), 1, 2, 3, 4, 5)); + .addOption(new NumericOption("radius", Material.STICK, TranslatedComponent.byId("optionCommon#radius"), 20, 30, 50, 60)) + .addOption(new NumericOption("levels", Material.SCAFFOLDING, TranslatedComponent.byId("game_TntRun#levels"), 1, 2, 3, 4, 5)); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java index 4f229a2..f736463 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/Towerdefense.java @@ -3,23 +3,74 @@ package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense; import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator; -import eu.mhsl.minenet.minigames.score.NoScore; +import eu.mhsl.minenet.minigames.score.LastWinsScore; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.EntityType; import net.minestom.server.entity.Player; -import net.minestom.server.item.ItemStack; -import net.minestom.server.item.Material; +import net.minestom.server.instance.batch.AbsoluteBlockBatch; +import net.minestom.server.instance.block.Block; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; public class Towerdefense extends StatelessGame { + private final Random random = new Random(); + private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch(); + private final List mazePath = new ArrayList<>(); + private List instances = new ArrayList<>(); + public Towerdefense() { - super(Dimension.NETHER.key, "Towerdefense", new NoScore()); + super(Dimension.NETHER.key, "Towerdefense", new LastWinsScore()); setGenerator(new MazeGenerator()); + this.generateMaze(); + } + + private void generateMaze() { + Pos position = new Pos(0, 0, 0); + this.addMazePosition(position, Block.GREEN_WOOL); + + List previousDirections = new ArrayList<>(); + int direction = 1; // 0 -> right; 1 -> straight; 2 -> left + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 3; j++) { + position = position.add(direction-1,0,direction%2); + this.addMazePosition(position, Block.WHITE_WOOL); + } + + int origin = 0; + int bound = 3; + long rightLeftDifference = previousDirections.stream().filter(integer -> integer == 0).count() - previousDirections.stream().filter(integer -> integer == 2).count(); + if(rightLeftDifference >= 2 || direction == 2) origin = 1; + if(rightLeftDifference <= -2 || direction == 0) bound = 2; + direction = this.random.nextInt(origin, bound); + previousDirections.add(direction); + } + this.addMazePosition(position, Block.WHITE_WOOL); + this.addMazePosition(position.add(0,0,1), Block.WHITE_WOOL); + this.addMazePosition(position.add(0,0,2), Block.RED_WOOL); + } + + private void addMazePosition(Pos position, Block pathBlock) { + this.mazeBatch.setBlock(position, pathBlock); + this.mazePath.add(position.add(0.5,1,0.5)); + } + + public AbsoluteBlockBatch getMazeBatch() { + return this.mazeBatch; + } + + public List getMazePath() { + return this.mazePath; } @Override protected boolean onPlayerJoin(Player p) { - p.getInventory().setItemStack(1, ItemStack.of(Material.ARMOR_STAND)); + TowerdefenseRoom newRoom = new TowerdefenseRoom(p, this); + this.instances.add(newRoom); + p.setInstance(newRoom); + newRoom.startWave(List.of(EntityType.ENDERMAN, EntityType.BLAZE, EntityType.PLAYER, EntityType.HORSE, EntityType.ARMOR_STAND, EntityType.SKELETON)); return false; } - - } diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/TowerdefenseRoom.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/TowerdefenseRoom.java new file mode 100644 index 0000000..9b02e0f --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/towerdefense/TowerdefenseRoom.java @@ -0,0 +1,58 @@ +package eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense; + +import eu.mhsl.minenet.minigames.instance.Dimension; +import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator; +import eu.mhsl.minenet.minigames.util.BatchUtil; +import net.minestom.server.MinecraftServer; +import net.minestom.server.entity.*; +import net.minestom.server.entity.attribute.Attribute; +import net.minestom.server.instance.InstanceContainer; +import net.minestom.server.item.ItemStack; +import net.minestom.server.item.Material; +import net.minestom.server.timer.TaskSchedule; + +import java.util.List; +import java.util.UUID; + +public class TowerdefenseRoom extends InstanceContainer { + private final Player player; + private final Towerdefense game; + + public TowerdefenseRoom(Player player, Towerdefense game) { + super(UUID.randomUUID(), Dimension.OVERWORLD.key); + MinecraftServer.getInstanceManager().registerInstance(this); + this.player = player; + this.game = game; + this.player.setGameMode(GameMode.ADVENTURE); + this.player.setAllowFlying(true); + this.player.getInventory().setItemStack(0, ItemStack.of(Material.ARMOR_STAND)); + + setGenerator(new MazeGenerator()); + BatchUtil.loadAndApplyBatch(this.game.getMazeBatch(), this, () -> {}); + } + + public void startWave(List entities) { + int counter = 0; + for(EntityType entityType : entities) { + MinecraftServer.getSchedulerManager().scheduleTask(() -> { + this.addEntity(new EntityCreature(entityType)); + return TaskSchedule.stop(); + }, TaskSchedule.millis(800L*counter)); + counter++; + } + } + + private void addEntity(EntityCreature entity) { + entity.setInstance(this, this.game.getMazePath().getFirst()); + entity.getAttribute(Attribute.MOVEMENT_SPEED).setBaseValue(0.15); + entity.getNavigator().setPathTo(this.game.getMazePath().get(1), 0.7, () -> changeEntityGoal(entity, 1)); + } + + private void changeEntityGoal(EntityCreature entity, int positionIndex) { + if(positionIndex == this.game.getMazePath().size()-1) { + return; + } + entity.getNavigator().setPathTo(this.game.getMazePath().get(positionIndex+1), 0.7, () -> changeEntityGoal(entity, positionIndex+1)); + } + +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/trafficlightrace/TrafficLightRaceFactory.java b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/trafficlightrace/TrafficLightRaceFactory.java index 83eaded..cf78b58 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/trafficlightrace/TrafficLightRaceFactory.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/game/stateless/types/trafficlightrace/TrafficLightRaceFactory.java @@ -17,6 +17,11 @@ public class TrafficLightRaceFactory implements GameFactory { return TranslatedComponent.byId("game_TrafficlightRace#name"); } + @Override + public TranslatedComponent description() { + return TranslatedComponent.byId("game_TrafficlightRace#description"); + } + @Override public ConfigManager configuration() { return new ConfigManager() diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java b/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java index 992d98f..4bd4e81 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/room/Room.java @@ -41,7 +41,7 @@ public class Room extends MineNetInstance implements Spawnable { } public static void deleteRoom(Room room) { - logger.info("Deleting room " + room.uniqueId); + logger.info("Deleting room " + room.uuid); rooms.remove(room); players.values().removeAll(Collections.singleton(room)); room.getAllMembers().forEach(player -> MoveInstance.move(player, Hub.INSTANCE)); @@ -65,6 +65,7 @@ public class Room extends MineNetInstance implements Spawnable { p.clearTitle(); p.getInventory().clear(); p.setGameMode(GameMode.ADVENTURE); + p.setInvisible(false); rooms.add(room); players.put(p, room); MoveInstance.move(p, room); diff --git a/src/main/java/eu/mhsl/minenet/minigames/instance/transfer/Transfer.java b/src/main/java/eu/mhsl/minenet/minigames/instance/transfer/Transfer.java index c6d102e..1b527c7 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/instance/transfer/Transfer.java +++ b/src/main/java/eu/mhsl/minenet/minigames/instance/transfer/Transfer.java @@ -19,4 +19,4 @@ public class Transfer extends MineNetInstance implements Spawnable { public Pos getSpawn() { return new Pos(0.5, 1, 0.5); } -} +} \ No newline at end of file diff --git a/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java b/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java index e890fad..66bf0bb 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java +++ b/src/main/java/eu/mhsl/minenet/minigames/lang/Languages.java @@ -28,10 +28,10 @@ public class Languages { } public Lang getLanguage(Player p) { - return getLanguage(p.getSettings().getLocale()); + return getLanguage(p.getSettings().locale().toString()); // TODO funktioniert die locale noch? } public Lang getLanguage(String mapId) { - return languages.computeIfAbsent(mapId, unused -> languages.computeIfAbsent(defaultLanguage, (key) -> new DummyLang())); + return languages.computeIfAbsent(mapId.toLowerCase(), unused -> languages.computeIfAbsent(defaultLanguage, (key) -> new DummyLang())); } private void readAll() { diff --git a/src/main/java/eu/mhsl/minenet/minigames/score/LastWinsScore.java b/src/main/java/eu/mhsl/minenet/minigames/score/LastWinsScore.java index c2bae16..c37cb96 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/score/LastWinsScore.java +++ b/src/main/java/eu/mhsl/minenet/minigames/score/LastWinsScore.java @@ -10,7 +10,7 @@ import java.util.Set; public class LastWinsScore extends Score { @Override public void insertResultImplementation(Set p) { - getScores().add(0, p); + getScores().addFirst(p); } @Override diff --git a/src/main/java/eu/mhsl/minenet/minigames/score/PointsWinScore.java b/src/main/java/eu/mhsl/minenet/minigames/score/PointsWinScore.java new file mode 100644 index 0000000..d9190f7 --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/score/PointsWinScore.java @@ -0,0 +1,65 @@ +package eu.mhsl.minenet.minigames.score; + +import eu.mhsl.minenet.minigames.message.Icon; +import eu.mhsl.minenet.minigames.message.TranslatableMessage; +import eu.mhsl.minenet.minigames.message.type.ChatMessage; +import eu.mhsl.minenet.minigames.message.type.TitleMessage; +import eu.mhsl.minenet.minigames.util.MapUtil; +import net.minestom.server.entity.Player; + +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; + +public class PointsWinScore extends Score { + private Map, Integer> scoreOrder = new HashMap<>(); + + @Override + protected void insertResultImplementation(Set p, int currentPoints) { + this.scoreOrder.put(p, currentPoints); + this.scoreOrder = MapUtil.sortByValue(this.scoreOrder); + getScores().clear(); + this.scoreOrder.forEach((player, integer) -> getScores().addFirst(player)); + } + + @Override + protected void insertResultImplementation(Set p) { + this.insertResultImplementation(p, 0); + } + + @Override + public void setDone() { + if(isClosed()) return; + close(); + new ChatMessage(Icon.STAR, true) + .appendTranslated("score#result") + .newLine() + .indent() + .numberedList( + getScores() + .stream() + .filter(players -> !players.stream() + .filter(player -> !player.getUsername().isBlank()) + .toList() + .isEmpty()) + .map(players -> players + .stream() + .filter(player -> scoreOrder.get(Set.of(player)) != null) + .map(player -> player.getUsername()+" : "+scoreOrder.get(Set.of(player)).toString()) + .collect(Collectors.joining(", ")) + ) + .toList() + ) + .undent() + .newLine() + .appendTranslated("score#thanks") + .send(instance.getPlayers()); + + instance.stop(); + } + + @Override + protected TranslatableMessage scoreMessage() { + return new TitleMessage(Duration.ofMillis(1000), Duration.ofSeconds(1)).appendTranslated("score#death"); + } +} diff --git a/src/main/java/eu/mhsl/minenet/minigames/score/Score.java b/src/main/java/eu/mhsl/minenet/minigames/score/Score.java index 92d7385..dce3e92 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/score/Score.java +++ b/src/main/java/eu/mhsl/minenet/minigames/score/Score.java @@ -8,6 +8,7 @@ import net.minestom.server.entity.Player; import net.minestom.server.event.instance.AddEntityToInstanceEvent; import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent; import net.minestom.server.event.player.PlayerDisconnectEvent; +import org.apache.commons.lang3.NotImplementedException; import java.util.ArrayList; import java.util.List; @@ -22,10 +23,19 @@ public abstract class Score { public Score() {} - public Score(int ignoreLastPlayers) { - this.ignoreLastPlayers = ignoreLastPlayers; + protected abstract void insertResultImplementation(Set p); + + protected void insertResultImplementation(Set p, int points) { + throw new NotImplementedException("This Score type is not able to process points"); } + public void insertResult(Player p) { + this.insertResultProcessor(p, () -> this.insertResultImplementation(Set.of(p))); + } + + public void insertResult(Player p, int points) { + this.insertResultProcessor(p, () -> this.insertResultImplementation(Set.of(p), points)); + } public void attachListeners() { this.instance.eventNode() @@ -45,15 +55,8 @@ public abstract class Score { setDone(); } } - protected abstract void insertResultImplementation(Set p); - protected abstract TranslatableMessage scoreMessage(); - public void insertResult(Player p) { - if(hasResult(p)) return; - this.scoreMessage().send(p); - this.insertResultImplementation(Set.of(p)); - this.checkGameEnd(); - } + protected abstract TranslatableMessage scoreMessage(); public void insertRemainingPlayers(Set players) { this.insertResultImplementation(players.stream().filter(p -> !hasResult(p)).collect(Collectors.toSet())); @@ -93,10 +96,19 @@ public abstract class Score { instance.stop(); } + private void insertResultProcessor(Player p, Runnable callback) { + if(hasResult(p)) return; + this.scoreMessage().send(p); + callback.run(); + this.checkGameEnd(); + } + public boolean isClosed() { return isClosed; } + public void close() { isClosed = true; } + protected void onGameEnd() { this.instance.stop(); } diff --git a/src/main/java/eu/mhsl/minenet/minigames/score/tournament/TournamentDisplay.java b/src/main/java/eu/mhsl/minenet/minigames/score/tournament/TournamentDisplay.java index f1398de..c917e14 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/score/tournament/TournamentDisplay.java +++ b/src/main/java/eu/mhsl/minenet/minigames/score/tournament/TournamentDisplay.java @@ -19,9 +19,9 @@ public class TournamentDisplay extends MineNetInstance implements Spawnable { private final Tournament tournament; private final Pos[] placePositions = new Pos[] { - new Pos(8.5, -57, 21.5, 180, 0), - new Pos(9.5, -58, 21.5, 180, 0), - new Pos(7.5, -59, 21.5, 180, 0) + new Pos(8.5, -56, 22.5, 180, 0), + new Pos(12.5, -57, 20.5, 180, 0), + new Pos(4.5, -58, 20.5, 180, 0) }; public TournamentDisplay(Tournament tournament) { @@ -30,6 +30,8 @@ public class TournamentDisplay extends MineNetInstance implements Spawnable { this.places = tournament.getPlaces(); this.tournament = tournament; + this.places.forEach(player -> player.setGameMode(GameMode.ADVENTURE)); + eventNode().addListener(PlayerMoveEvent.class, playerMoveEvent -> { if(isOnDisplay(playerMoveEvent.getPlayer()) && !playerMoveEvent.getNewPosition().sameBlock(placePositions[getRankPosition(playerMoveEvent.getPlayer())])) { playerMoveEvent.setCancelled(true); @@ -62,6 +64,6 @@ public class TournamentDisplay extends MineNetInstance implements Spawnable { @Override public Pos getSpawn() { - return new Pos(8.5, -55, 8.5); + return new Pos(8.5, -59, 11.5); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/server/provider/ByPlayerNameUuidProvider.java b/src/main/java/eu/mhsl/minenet/minigames/server/provider/ByPlayerNameUuidProvider.java deleted file mode 100644 index c019bba..0000000 --- a/src/main/java/eu/mhsl/minenet/minigames/server/provider/ByPlayerNameUuidProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -package eu.mhsl.minenet.minigames.server.provider; - -import eu.mhsl.minenet.minigames.util.UuidUtil; -import net.minestom.server.MinecraftServer; -import net.minestom.server.network.UuidProvider; -import net.minestom.server.network.player.PlayerConnection; -import net.minestom.server.utils.mojang.MojangUtils; - -import java.util.UUID; -import java.util.logging.Logger; - -public class ByPlayerNameUuidProvider implements UuidProvider { - @Override - public UUID provide(PlayerConnection playerConnection, String username) { - try { - - if(MinecraftServer.getConnectionManager().findOnlinePlayer(username) != null) throw new IllegalStateException(); - String client = MojangUtils.fromUsername(username).get("id").getAsString(); - return UuidUtil.unTrimm(client); - - } catch (NullPointerException e) { - Logger.getGlobal().info("Player " + username + " is unknown by Mojang! (Using random UUID)"); - } catch (IllegalStateException e) { - Logger.getGlobal().info("Player with the username " + username + " is already online."); - playerConnection.disconnect(); - } - - return UUID.randomUUID(); - } -} diff --git a/src/main/java/eu/mhsl/minenet/minigames/shared/entity/InteractableEntity.java b/src/main/java/eu/mhsl/minenet/minigames/shared/entity/InteractableEntity.java index e7cbf1a..70e6d86 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/shared/entity/InteractableEntity.java +++ b/src/main/java/eu/mhsl/minenet/minigames/shared/entity/InteractableEntity.java @@ -28,10 +28,6 @@ public class InteractableEntity extends EntityCreature { .addListener(PlayerEntityInteractEvent.class, this::onInteract) //TODO Why are some of these listeners not working? .addListener(EntityAttackEvent.class, this::onAttack); - - System.out.println(eventNode().getChildren()); - - System.out.println(eventNode().getParent().getChildren()); } private void setInstanceEvent(@NotNull AddEntityToInstanceEvent addEntityToInstanceEvent) { diff --git a/src/main/java/eu/mhsl/minenet/minigames/skin/SkinCache.java b/src/main/java/eu/mhsl/minenet/minigames/skin/SkinCache.java index 681ba4a..4325232 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/skin/SkinCache.java +++ b/src/main/java/eu/mhsl/minenet/minigames/skin/SkinCache.java @@ -3,7 +3,6 @@ package eu.mhsl.minenet.minigames.skin; import net.minestom.server.MinecraftServer; import net.minestom.server.entity.Player; import net.minestom.server.entity.PlayerSkin; -import net.minestom.server.timer.ExecutionType; import net.minestom.server.timer.TaskSchedule; import java.util.HashMap; @@ -12,19 +11,15 @@ import java.util.Map; public class SkinCache { private static final Map skins = new HashMap<>(); - public static PlayerSkin getSkin(Player p) { - return SkinCache.getSkin(p.getUsername()); - } - public static PlayerSkin getSkin(String p) { if(!skins.containsKey(p)) skins.put(p, PlayerSkin.fromUsername(p)); return skins.get(p); } public static void applySkin(Player p) { - MinecraftServer.getSchedulerManager().submitTask(() -> { + MinecraftServer.getSchedulerManager().scheduleTask(() -> { p.setSkin(SkinCache.getSkin(p.getUsername())); return TaskSchedule.stop(); - }, ExecutionType.TICK_END); + }, TaskSchedule.millis(500)); } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/util/InventoryItemAlignment.java b/src/main/java/eu/mhsl/minenet/minigames/util/InventoryItemAlignment.java index 69f02cf..283cfa1 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/util/InventoryItemAlignment.java +++ b/src/main/java/eu/mhsl/minenet/minigames/util/InventoryItemAlignment.java @@ -130,25 +130,10 @@ public class InventoryItemAlignment { }.get(count); } - public static class ItemOffset { - private final int x; - private final int z; - - public ItemOffset(int x, int z) { - this.x = x; - this.z = z; - } - - public int getX() { - return x; - } - - public int getZ() { - return z; - } + public record ItemOffset(int x, int z) { public int get() { - return x + (z * 9); + return x + (z * 9); + } } - } } diff --git a/src/main/java/eu/mhsl/minenet/minigames/util/MapUtil.java b/src/main/java/eu/mhsl/minenet/minigames/util/MapUtil.java new file mode 100644 index 0000000..ac5506b --- /dev/null +++ b/src/main/java/eu/mhsl/minenet/minigames/util/MapUtil.java @@ -0,0 +1,20 @@ +package eu.mhsl.minenet.minigames.util; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class MapUtil { + public static > Map sortByValue(Map map) { + List> list = new ArrayList<>(map.entrySet()); + list.sort(Map.Entry.comparingByValue()); + + Map result = new LinkedHashMap<>(); + for (Map.Entry entry : list) { + result.put(entry.getKey(), entry.getValue()); + } + + return result; + } +} \ No newline at end of file diff --git a/src/main/java/eu/mhsl/minenet/minigames/util/PacketUtil.java b/src/main/java/eu/mhsl/minenet/minigames/util/PacketUtil.java deleted file mode 100644 index 85deba8..0000000 --- a/src/main/java/eu/mhsl/minenet/minigames/util/PacketUtil.java +++ /dev/null @@ -1,39 +0,0 @@ -package eu.mhsl.minenet.minigames.util; - -import net.minestom.server.MinecraftServer; -import net.minestom.server.entity.GameMode; -import net.minestom.server.entity.Player; -import net.minestom.server.entity.PlayerSkin; -import net.minestom.server.network.packet.server.play.PlayerInfoUpdatePacket; - -import java.util.List; - -public class PacketUtil { - public static void resendPlayerList(Player p) { - MinecraftServer.getConnectionManager().getOnlinePlayers().forEach(player -> { - if(player.getUuid().equals(p.getUuid())) return; - - final PlayerSkin skin = player.getSkin(); - List properties = - skin != null ? List.of(new PlayerInfoUpdatePacket.Property("textures", skin.textures(), skin.signature())) : List.of(); - - p.sendPacket( - new PlayerInfoUpdatePacket(PlayerInfoUpdatePacket.Action.ADD_PLAYER, - new PlayerInfoUpdatePacket.Entry( - player.getUuid(), - player.getUsername(), - properties, - true, - 0, - GameMode.SURVIVAL, - null, - null - ) - ) - ); -// p.sendPacket( //TODO spawnPlayerPacket does no longer exists -// new SpawnPlayerPacket(player.getEntityId(), player.getUuid(), player.getPosition()) -// ); - }); - } -} diff --git a/src/main/java/eu/mhsl/minenet/minigames/util/Static.java b/src/main/java/eu/mhsl/minenet/minigames/util/Static.java deleted file mode 100644 index 3472707..0000000 --- a/src/main/java/eu/mhsl/minenet/minigames/util/Static.java +++ /dev/null @@ -1,5 +0,0 @@ -package eu.mhsl.minenet.minigames.util; - -public abstract class Static { - public abstract void load(); //TODO REMOVE -} diff --git a/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/SquarePlateTerrainGenerator.java b/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/SquarePlateTerrainGenerator.java index 0eca155..30bfd3b 100644 --- a/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/SquarePlateTerrainGenerator.java +++ b/src/main/java/eu/mhsl/minenet/minigames/world/generator/terrain/SquarePlateTerrainGenerator.java @@ -67,7 +67,7 @@ public class SquarePlateTerrainGenerator extends PlateTerrainGenerator { } if(generateBorders) { - Runnable generateBorder = () -> unit.modifier().fill(bottom, bottom.add(1, 0, 1), Block.BARRIER); // TODO DimensionType.OVERWORLD.getMaxY() is now hardcoded 0, might break behaviour + Runnable generateBorder = () -> unit.modifier().fill(bottom, bottom.add(1, DimensionType.VANILLA_MAX_Y, 1), Block.BARRIER); if(bottom.z() <= length+1 && bottom.z() >= -1 && bottom.x() >= -1 && bottom.x() <= width+1) { if(bottom.x() == -1 || bottom.x() == width+1) { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 9f18e11..39956b0 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -17,4 +17,5 @@ api: port: 8080 admins: - - minetec \ No newline at end of file + - minetec + - 28Pupsi28 \ No newline at end of file diff --git a/src/main/resources/lang/locales.map.csv b/src/main/resources/lang/locales.map.csv index fa4deca..d0e2435 100644 --- a/src/main/resources/lang/locales.map.csv +++ b/src/main/resources/lang/locales.map.csv @@ -23,7 +23,11 @@ other_description;Games which does not fit into other Categories;Spiele welche n pvp;Player vs Player;Spieler gegen Spieler pvp_description;Fight against other Players;Kämpfe gegen andere Spieler pve;Player vs Enviroment;Spieler gegen Umwelt -pve_description;Surivie the world or fight entities;Überlebe die Welt oder kämpfe gegen Mobs +pve_description;Survive the world or fight entities;Überlebe die Welt oder kämpfe gegen Mobs +jumpnrun;jump and run;jump and run +jumpnrun_description;Proof your jump-and-run skills;Stelle deine jump-and-run Fähigkeiten unter Beweis +prototype;Prototype;Prototyp +prototype_description;Prototype games;Prototyp Spiele ;; ns:hub#;; invTitle;MineNet Servernetwork;MineNet Servernetzwerk @@ -36,7 +40,7 @@ join_notFound;Lobby not found: ;Lobby konnte nicht gefunden werden: ;; ns:score#;; result;Results;Ergebnisse -thanks;Thank you for Playing;Danke für's spielen +thanks;Thank you for Playing;Danke für's Spielen finish;You did it;Du hast es geschafft death;You are out;Du hast verloren done;Finish;Fertig @@ -65,15 +69,26 @@ noOptionDescription;There are no options for this Game;Es gibt keine Einstellung ns:GameFactory#;; missingDescription;No description;Keine Beschreibung ;; +ns:game_TntRun#;; +name;TNT run;TNT Rennen +description;The tnt at the ground disappears, don't fall;Das TNT am Boden verschwindet, falle nicht herunter +levels;levels;Ebenen +;; +ns:game_ElytraRace#;; +name;Elytra race;Elytra Rennen +description;Be fast while flying through the rings;Sei schnell während du durch die Ringe fliegst +ringCount;ring count;Anzahl der Ringe +;; ns:game_Minerun#;; name;Minerun;Minenrennen description;Ditch deadly Mines in the ground an be the first in the goal;Weiche den tödlichen Bodenmienen aus und sei der erste im Ziel -optionPercentageMines;Percentage of Miens;Prozentsatz der Minen +optionPercentageMines;Percentage of mines;Prozentsatz der Minen ;; ns:game_Deathcube#;; name;Deathcube;Todeswürfel description;Find a way to jump higher and be the first on the top;Finde einen weg nach oben und sei der erste im Ziel optionPercentageBlocks;Percentage of Blocks;Prozentsatz der Blöcke +optionPvpEnabled;pvp enabled;PvP aktiviert ;; ns:game_Stickfight#;; name;Stickfight;Stockschlacht @@ -83,6 +98,14 @@ ns:game_TrafficlightRace#;; name;Red light green light;Rotes licht, Grünes licht description;Only go forward if the Trafficlights show green;Gehe nur bei Grün vorran ;; +ns:game_AcidRain#;; +name;Acid rain;Säureregen +description;Stay under the holey roof to dodge acid rain;Bleib unter dem löchrigen Dach um dem sauren Regen auszuweichen +;; +ns:game_BowSpleef#;; +name;Bow spleef;Bogen Spleef +description;Spleef other players and be the last survivor;Zerstöre Blöcke unter anderen Spielern und sei der letzte im Feld +;; ns:game_Towerdefense#;; name;Towerdefense;Towerdefense description;Protect the path ????;?????? @@ -91,7 +114,19 @@ ns:game_Spleef#;; name;Spleef;Spleef; description;Spleef other players and be the last survivor;Zerstöre Blöcke unter anderen Spielern und sei der letzte im Feld shovelName;Snow thrower;Schneeflug +stackCount;levels;Ebenen +;; +ns:game_Tetris#;; +name;Tetris;Tetris +description;Sort falling blocks and clear lines;Sortiere fallende Blöcke und kläre Linien +nextTetrominoesCount;Number of upcoming Tetrominos displayed;Anzahl der angezeigten kommenden Tetrominos +isFast;Fast mode;Schneller Modus +hasCombat;Competitive mode;Kompetitiver Modus ;; ns:game_AnvilRun#;; -name;Anvil Run;Anvil Run -description;Run away from falling anvils;Renne von fallenden Ambossen davon \ No newline at end of file +name;Anvil run;Anvil run +description;Run away from falling anvils;Renne von fallenden Ambossen davon +;; +ns:game_jumpDive#;; +name;Jump dive;Wassersprung +description;Jump into the water, avoiding already used spots!;Springe ins wasser an stellen, in denen noch niemand zuvor gelandet ist! diff --git a/src/main/resources/maps/resultdisplay/region/r.-1.-1.mca b/src/main/resources/maps/resultdisplay/region/r.-1.-1.mca index e2f33c3..80f6f9a 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-1.-1.mca and b/src/main/resources/maps/resultdisplay/region/r.-1.-1.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.-1.-2.mca b/src/main/resources/maps/resultdisplay/region/r.-1.-2.mca index 81c018d..fe2c6a7 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-1.-2.mca and b/src/main/resources/maps/resultdisplay/region/r.-1.-2.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.-1.0.mca b/src/main/resources/maps/resultdisplay/region/r.-1.0.mca index 2826910..1d60fab 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-1.0.mca and b/src/main/resources/maps/resultdisplay/region/r.-1.0.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.-1.1.mca b/src/main/resources/maps/resultdisplay/region/r.-1.1.mca index 75a841e..4a235e3 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-1.1.mca and b/src/main/resources/maps/resultdisplay/region/r.-1.1.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.-2.-1.mca b/src/main/resources/maps/resultdisplay/region/r.-2.-1.mca index 00a3d48..191ca52 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-2.-1.mca and b/src/main/resources/maps/resultdisplay/region/r.-2.-1.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.-2.-2.mca b/src/main/resources/maps/resultdisplay/region/r.-2.-2.mca index 0275b48..f3119de 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-2.-2.mca and b/src/main/resources/maps/resultdisplay/region/r.-2.-2.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.-2.0.mca b/src/main/resources/maps/resultdisplay/region/r.-2.0.mca index 4293674..18a1e0a 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-2.0.mca and b/src/main/resources/maps/resultdisplay/region/r.-2.0.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.-2.1.mca b/src/main/resources/maps/resultdisplay/region/r.-2.1.mca index 4372bc7..24df0fe 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.-2.1.mca and b/src/main/resources/maps/resultdisplay/region/r.-2.1.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.0.-1.mca b/src/main/resources/maps/resultdisplay/region/r.0.-1.mca index 9d5244d..d691ca4 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.0.-1.mca and b/src/main/resources/maps/resultdisplay/region/r.0.-1.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.0.-2.mca b/src/main/resources/maps/resultdisplay/region/r.0.-2.mca index 4112e71..4f37c79 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.0.-2.mca and b/src/main/resources/maps/resultdisplay/region/r.0.-2.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.0.0.mca b/src/main/resources/maps/resultdisplay/region/r.0.0.mca index 4450d36..cfc309f 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.0.0.mca and b/src/main/resources/maps/resultdisplay/region/r.0.0.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.0.1.mca b/src/main/resources/maps/resultdisplay/region/r.0.1.mca index 99d3d69..6b027a4 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.0.1.mca and b/src/main/resources/maps/resultdisplay/region/r.0.1.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.1.-1.mca b/src/main/resources/maps/resultdisplay/region/r.1.-1.mca index 7032780..6885c84 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.1.-1.mca and b/src/main/resources/maps/resultdisplay/region/r.1.-1.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.1.-2.mca b/src/main/resources/maps/resultdisplay/region/r.1.-2.mca index 6267663..f2c69d3 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.1.-2.mca and b/src/main/resources/maps/resultdisplay/region/r.1.-2.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.1.0.mca b/src/main/resources/maps/resultdisplay/region/r.1.0.mca index 27a3067..86ae28c 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.1.0.mca and b/src/main/resources/maps/resultdisplay/region/r.1.0.mca differ diff --git a/src/main/resources/maps/resultdisplay/region/r.1.1.mca b/src/main/resources/maps/resultdisplay/region/r.1.1.mca index 52490b7..cf7a1a4 100644 Binary files a/src/main/resources/maps/resultdisplay/region/r.1.1.mca and b/src/main/resources/maps/resultdisplay/region/r.1.1.mca differ