53 Commits

Author SHA1 Message Date
Pupsi 1a9b9f5fd2 added return to player race position check 2026-07-31 13:28:27 +02:00
jannis b0cf527977 removed unnecessary comments 2026-07-29 15:59:08 +02:00
jannis 01646546a3 fixen issues with playerrace and changed the item selection in pillars 2026-07-26 22:03:11 +02:00
jannis 13a0cdde3a changed gametype of playerrace to jumpnrun and changed symbol to dirt path in playerrace 2026-07-20 21:12:33 +02:00
jannis 7d60c74d85 fixed generation ofo elytrarace 2026-07-20 13:47:25 +02:00
MineTec 0d7f1fc646 updated dependencies and updated all usages accordingly, replaced deprecated methods, and updated project to JDK 25 2026-07-19 19:37:44 +02:00
jannis ee421245d1 added translations 2026-07-19 01:50:34 +02:00
jannis d75332927c fixed that something can generate twice in playerrace 2026-07-19 01:31:53 +02:00
jannis 2c579f4afe fixen generating problem and finished logic of playerrace 2026-07-19 01:11:56 +02:00
jannis 82984cf5cf added LadderWall2 and added checkpoints 2026-07-19 00:16:12 +02:00
MineTec 14f6a38212 merged origin/develop-jannis 2026-07-18 22:15:03 +02:00
MineTec 3bdc4113c0 added 20 obstacle types, adjustable track length and forked obstacle generation 2026-07-18 22:14:29 +02:00
jannis b7e292fe0e added LadderWall 2026-07-18 21:35:00 +02:00
Pupsi 57cd5acdfd added cos function to mudpit 2026-07-18 18:57:33 +02:00
Pupsi 0f72b8049d simplified block loop from generator 2026-07-18 18:07:20 +02:00
jannis 9992b07ed5 added basic obstacle mechanic 2026-07-18 17:41:45 +02:00
jannis f55148203e started player race 2026-07-18 15:15:29 +02:00
jannis 9bdb98cdc9 other way for the effective-final 2026-07-13 16:34:07 +02:00
jannis 6f0ebe4d12 moved colorjump from prototype to jumpnrun 2026-07-13 10:40:58 +02:00
jannis 58983be156 removed off-hand swap in colorjump 2026-07-13 10:30:34 +02:00
jannis ee7d434d7e perfectet colorjump timer, cancelled inventory movement 2026-02-16 22:28:32 +01:00
jannis 410f7b4027 perfekted colorjump 2026-02-16 21:07:30 +01:00
jannis e1515200ab stoped task at game end 2026-02-11 20:56:26 +01:00
jannis 27005f4fc4 Merge pull request 'added color jump' (#12) from develop-colorjump into develop
Reviewed-on: #12
Reviewed-by: Lars Neuhaus <larslukasneuhaus@gmx.de>
2026-02-10 20:24:17 +00:00
jannis 2808393c23 solved pr comments 2026-02-10 21:22:32 +01:00
jannis d0825695b3 added an end to color jump 2026-02-10 19:23:06 +01:00
jannis e822a5c5e2 fixed translations for color jump 2026-02-10 19:17:37 +01:00
jannis bf95c8dcc2 added translations to color jump 2026-02-10 19:12:36 +01:00
jannis 2f70b25e87 added ColorJump 2026-02-10 19:08:07 +01:00
Pupsi cbec1ea7f8 Merge pull request 'develop-tetris-srs' (#11) from develop-tetris-srs into develop
Reviewed-on: #11
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2026-02-06 10:47:27 +00:00
Pupsi c176cfaf2c solved pr comments; switched from hard lock task to reset counter 2026-02-04 19:25:03 +01:00
Pupsi 56f56d48b6 added airborne lock timer reset 2026-02-03 19:22:33 +01:00
Pupsi 991e51bc10 fixed ground behavior for tetrominoes 2026-02-03 19:08:05 +01:00
Pupsi ffba86ac41 removed cause of tetris collision bugs (yaw, pitch in Pos) 2026-02-03 17:18:28 +01:00
Pupsi f95c670514 removed cause of tetris collision bugs (yaw, pitch in Pos) 2026-02-03 17:18:10 +01:00
Pupsi 0b3c757ce0 fixed srs rotations 2026-02-03 15:51:41 +01:00
Pupsi 76df6643db added simplified SRS; not functional for I-Tetromino 2026-02-02 22:37:43 +01:00
jannis 7186b4dbea Merge pull request 'added pillars and block battle' (#10) from develop-pillars into develop
Reviewed-on: #10
Reviewed-by: Lars Neuhaus <larslukasneuhaus@gmx.de>
2026-02-02 19:09:44 +00:00
jannis 24d79d7a11 pull requestest changes 2026-02-02 20:04:55 +01:00
jannis 70fd5bafdb updated Block Battle description 2026-02-02 18:19:45 +00:00
jannis 11ddd01470 pull requestest changes 2026-02-02 19:15:46 +01:00
jannis 7d5fb025bd added setting, refactored code 2026-02-01 18:24:36 +01:00
jannis 05240660f2 added scores and translations 2026-02-01 17:37:37 +01:00
jannis d0031b4ea5 added BlockBattle 2026-02-01 14:14:34 +01:00
jannis 360266339d added pillars game 2026-01-23 20:26:04 +01:00
jannis 325fba2a53 Merge pull request 'added second option to game selection menu, finished stickfight(added timer to stick fight)' (#9) from develop-jannis into develop
Reviewed-on: #9
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2026-01-11 19:07:05 +00:00
jannis e760cdb2c6 removed 0 add option seconds 2026-01-10 21:41:45 +01:00
jannis c111c027ff added second option to game selection menu, finished stickfight 2026-01-10 17:41:19 +01:00
Pupsi 73ab137ae4 Merge remote-tracking branch 'origin/develop' into develop 2026-01-10 16:29:26 +01:00
Pupsi 9a9e646288 fixed Tetris camera shake 2026-01-10 16:29:12 +01:00
jannis 728dc92fc8 Merge pull request 'elytra race fix boost ring 1' (#8) from develop-jannis into develop
Reviewed-on: #8
2026-01-10 13:48:50 +00:00
jannis 2f8ff36e5e Merge branch 'develop' into develop-jannis 2026-01-10 13:48:39 +00:00
jannis 5ab42fde4b Merge pull request 'added stick to inventory for stick fight' (#7) from develop-stickfight into develop
Reviewed-on: #7
Reviewed-by: Elias Müller <elias@elias-mueller.com>
2026-01-10 13:10:26 +00:00
80 changed files with 2344 additions and 348 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK" /> <component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="openjdk-25" project-jdk-type="JavaSDK" />
</project> </project>
+17 -40
View File
@@ -3,7 +3,7 @@
plugins { plugins {
id 'java' id 'java'
id "com.github.johnrengelman.shadow" version "7.1.0" id "com.gradleup.shadow" version "9.6.0"
} }
group 'eu.mhsl.minenet' group 'eu.mhsl.minenet'
@@ -11,58 +11,33 @@ version '1.0-SNAPSHOT'
repositories { repositories {
mavenCentral() mavenCentral()
google()
maven {
url 'https://jitpack.io'
}
maven {
url "https://repo.unnamed.team/repository/unnamed-public/"
}
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots'
}
} }
allprojects {
repositories {
maven { url "https://jitpack.io" }
}
}
java { java {
toolchain { toolchain {
languageVersion = JavaLanguageVersion.of(21) languageVersion = JavaLanguageVersion.of(25)
} }
} }
dependencies { dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0' testImplementation 'org.junit.jupiter:junit-jupiter:6.1.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
//https://jitpack.io/#Minestom/Minestom //https://mvnrepository.com/artifact/net.minestom/minestom
implementation 'net.minestom:minestom-snapshots:fd51c8d17a' implementation 'net.minestom:minestom:2026.07.12-26.2'
//Tools //Tools
implementation 'de.articdive:jnoise:3.0.2' implementation 'de.articdive:jnoise-pipeline:4.1.0'
compileOnly 'it.unimi.dsi:fastutil:8.5.18' // von Minestom nur zur Laufzeit bereitgestellt, fuer BatchUtil-Reflection benoetigt
implementation 'net.md-5:bungeecord-config:1.21-R0.3' implementation 'net.md-5:bungeecord-config:1.21-R0.3'
implementation 'org.apache.commons:commons-text:1.10.0' implementation 'org.apache.commons:commons-text:1.15.0'
implementation 'org.spongepowered:configurate-yaml:4.1.2' implementation 'org.spongepowered:configurate-yaml:4.2.0'
implementation 'com.sparkjava:spark-core:2.9.4' implementation 'com.sparkjava:spark-core:2.9.4'
implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.google.code.gson:gson:2.14.0'
implementation 'com.google.guava:guava:32.0.1-android' implementation 'com.google.guava:guava:33.4.8-jre'
//PvP //PvP
implementation 'io.github.TogAr2:MinestomPvP:PR62-SNAPSHOT' implementation 'io.github.togar2:MinestomPvP:2026.05.30-26.1.1'
// Hephaestus engine
// implementation("team.unnamed:hephaestus-api:0.2.1-SNAPSHOT")
// implementation("team.unnamed:hephaestus-reader-blockbench:0.2.1-SNAPSHOT")
// implementation("team.unnamed:hephaestus-runtime-minestom:0.2.1-SNAPSHOT")
} }
tasks { tasks {
@@ -71,8 +46,6 @@ tasks {
attributes 'Main-Class': 'eu.mhsl.minenet.minigames.Main' attributes 'Main-Class': 'eu.mhsl.minenet.minigames.Main'
attributes 'Multi-Release': true attributes 'Multi-Release': true
} }
duplicatesStrategy = 'exclude'
from configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
} }
build { build {
dependsOn(shadowJar) dependsOn(shadowJar)
@@ -89,3 +62,7 @@ tasks.register('copyJarToServer', Exec) {
commandLine 'scp', 'build/libs/Minigames-1.0-SNAPSHOT.jar', 'root@10.20.6.5:/root/minigames' commandLine 'scp', 'build/libs/Minigames-1.0-SNAPSHOT.jar', 'root@10.20.6.5:/root/minigames'
} }
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:deprecation"
}
Binary file not shown.
+3 -1
View File
@@ -1,7 +1,9 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
networkTimeout=10000 networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored
+7 -11
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
############################################################################## ##############################################################################
# #
# Gradle start up script for POSIX generated by Gradle. # gradlew start up script for POSIX generated by Gradle.
# #
# Important for running: # Important for running:
# #
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole # bash, then to run this script, type that shell name before the whole
# command line, like: # command line, like:
# #
# ksh Gradle # ksh gradlew
# #
# Busybox and similar reduced shells will NOT work, because this script # Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features: # requires all of these POSIX shell features:
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # 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 APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -115,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -173,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -206,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # 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 # * 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. # treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.
Vendored
+12 -24
View File
@@ -19,12 +19,12 @@
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem gradlew startup script for Windows
@rem @rem
@rem ########################################################################## @rem ##########################################################################
@rem Set local scope for the variables with windows NT shell @rem Set local scope for the variables, and ensure extensions are enabled
if "%OS%"=="Windows_NT" setlocal setlocal EnableExtensions
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@@ -51,7 +51,7 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 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 echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:findJavaFromJavaHome :findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
@@ -65,30 +65,18 @@ echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 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 echo location of your Java installation. 1>&2
goto fail "%COMSPEC%" /c exit 1
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute gradlew
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* @rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
:end :exitWithErrorLevel
@rem End local scope for the variables with windows NT shell @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
if %ERRORLEVEL% equ 0 goto mainEnd "%COMSPEC%" /c exit %ERRORLEVEL%
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
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
:omega
@@ -3,12 +3,12 @@ package eu.mhsl.minenet.minigames;
import eu.mhsl.minenet.minigames.api.HttpServer; import eu.mhsl.minenet.minigames.api.HttpServer;
import eu.mhsl.minenet.minigames.command.Commands; import eu.mhsl.minenet.minigames.command.Commands;
import eu.mhsl.minenet.minigames.handler.Listeners; import eu.mhsl.minenet.minigames.handler.Listeners;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.lang.Languages; import eu.mhsl.minenet.minigames.lang.Languages;
import eu.mhsl.minenet.minigames.server.tasks.TablistUpdateTask; import eu.mhsl.minenet.minigames.server.tasks.TablistUpdateTask;
import net.minestom.server.Auth;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.extras.bungee.BungeeCordProxy;
import net.minestom.server.extras.lan.OpenToLAN; import net.minestom.server.extras.lan.OpenToLAN;
import net.minestom.server.extras.velocity.VelocityProxy;
import net.minestom.server.timer.TaskSchedule; import net.minestom.server.timer.TaskSchedule;
import org.spongepowered.configurate.ConfigurateException; import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.ConfigurationNode;
@@ -25,7 +25,7 @@ public class Main {
public static ConfigurationNode globalConfig; public static ConfigurationNode globalConfig;
@SuppressWarnings("ResultOfMethodCallIgnored") @SuppressWarnings("ResultOfMethodCallIgnored")
public static void main(String[] args) throws ConfigurateException { static void main(String[] args) throws ConfigurateException {
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
Resource.values(); // This initializes and preloads the enum and extracts the resources Resource.values(); // This initializes and preloads the enum and extracts the resources
Languages.getInstance(); //Preload languages into the jvm Languages.getInstance(); //Preload languages into the jvm
@@ -38,14 +38,31 @@ public class Main {
ConfigurationNode serverConfig = globalConfig.node("server"); ConfigurationNode serverConfig = globalConfig.node("server");
// muss vor MinecraftServer.init() gesetzt werden: ServerFlag liest die Properties statisch beim Klassenladen;
// bei fehlendem/ungueltigem Config-Wert den Minestom-Default behalten (0 wuerde keine Chunks senden)
int viewDistance = serverConfig.node("view-distance").getInt(0);
if(viewDistance > 0) {
System.setProperty("minestom.chunk-view-distance", String.valueOf(viewDistance));
}
logger.info("Initialize Minecraft server..."); logger.info("Initialize Minecraft server...");
MinecraftServer server = MinecraftServer.init(); Auth auth = new Auth.Offline();
if(globalConfig.node("bungeecord", "enabled").getBoolean()) {
auth = new Auth.Bungee();
} else if(globalConfig.node("velocity", "enabled").getBoolean()) {
auth = new Auth.Velocity(Objects.requireNonNull(globalConfig.node("velocity", "secret").getString()));
}
MinecraftServer server = MinecraftServer.init(auth);
// MinestomPvP.init(); // MinestomPvP.init();
// Dimensionen muessen vor server.start() registriert werden:
// seit Minestom 26.x sind die dynamischen Registries nach dem Start eingefroren
Dimension.values();
MinecraftServer.setBrandName("mhsl.eu - minenet - credits to minestom"); MinecraftServer.setBrandName("mhsl.eu - minenet - credits to minestom");
MinecraftServer.setCompressionThreshold(serverConfig.node("compression-threshold").getInt(0)); MinecraftServer.setCompressionThreshold(serverConfig.node("compression-threshold").getInt(0));
System.setProperty("minestom.chunk-view-distance", String.valueOf(serverConfig.node("view-distance").getInt()));
Commands.values(); Commands.values();
Listeners.values(); Listeners.values();
@@ -57,12 +74,6 @@ public class Main {
if(serverConfig.node("open-to-lan").getBoolean()) OpenToLAN.open(); if(serverConfig.node("open-to-lan").getBoolean()) OpenToLAN.open();
if(globalConfig.node("bungeecord", "enabled").getBoolean()) BungeeCordProxy.enable();
if(globalConfig.node("velocity", "enabled").getBoolean()) {
VelocityProxy.enable(Objects.requireNonNull(globalConfig.node("velocity", "secret").getString()));
}
server.start("0.0.0.0", serverConfig.node("port").getInt(25565)); server.start("0.0.0.0", serverConfig.node("port").getInt(25565));
System.gc(); System.gc();
@@ -35,7 +35,7 @@ public class SetMemorialCommand extends PrivilegedCommand {
.getTournament() .getTournament()
.setMemorialConfiguration( .setMemorialConfiguration(
new MemorialConfiguration( new MemorialConfiguration(
Material.fromNamespaceId(context.get(materialArgument)), Material.fromKey(context.get(materialArgument)),
context.get(titleArgument), context.get(titleArgument),
context.get(loreArgument) context.get(loreArgument)
) )
@@ -37,7 +37,7 @@ public class SetRewardCommand extends PrivilegedCommand {
.getTournament() .getTournament()
.setRewardConfiguration( .setRewardConfiguration(
new RewardConfiguration( new RewardConfiguration(
Material.fromNamespaceId(context.get(materialArgument)), Material.fromKey(context.get(materialArgument)),
Arrays.stream(context.get(amountsArgument)).map(Integer::valueOf).collect(Collectors.toList()) Arrays.stream(context.get(amountsArgument)).map(Integer::valueOf).collect(Collectors.toList())
) )
); );
@@ -2,6 +2,7 @@ package eu.mhsl.minenet.minigames.handler;
import eu.mhsl.minenet.minigames.handler.global.AddEntityToInstanceEventListener; import eu.mhsl.minenet.minigames.handler.global.AddEntityToInstanceEventListener;
import eu.mhsl.minenet.minigames.handler.global.ChatFormatHandler; import eu.mhsl.minenet.minigames.handler.global.ChatFormatHandler;
import eu.mhsl.minenet.minigames.handler.global.PlayerFreezeHandler;
import eu.mhsl.minenet.minigames.handler.global.PlayerLeaveHandler; import eu.mhsl.minenet.minigames.handler.global.PlayerLeaveHandler;
import eu.mhsl.minenet.minigames.handler.global.PlayerLoginHandler; import eu.mhsl.minenet.minigames.handler.global.PlayerLoginHandler;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
@@ -11,6 +12,7 @@ public enum Listeners {
SPAWN(new AddEntityToInstanceEventListener()), SPAWN(new AddEntityToInstanceEventListener()),
LOGIN(new PlayerLoginHandler()), LOGIN(new PlayerLoginHandler()),
LEAVE(new PlayerLeaveHandler()), LEAVE(new PlayerLeaveHandler()),
FREEZE(new PlayerFreezeHandler()),
CHAT(new ChatFormatHandler()); CHAT(new ChatFormatHandler());
Listeners(EventListener<?> event) { Listeners(EventListener<?> event) {
@@ -0,0 +1,43 @@
package eu.mhsl.minenet.minigames.handler.global;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventListener;
import net.minestom.server.event.player.PlayerTickEvent;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import org.jetbrains.annotations.NotNull;
/**
* Weder Minestom noch MinestomPvP verwalten die TICKS_FROZEN-Metadata. Der 26.x-Client baut das
* Freeze-Overlay bei Powder-Snow-Kontakt zwar selbst auf, der Abbau ist aber server-authoritative —
* ohne diese Vanilla-Logik bleibt das Overlay dauerhaft haengen (auch ueber Instanzwechsel hinweg).
*/
public class PlayerFreezeHandler implements EventListener<PlayerTickEvent> {
private static final int TICKS_REQUIRED_TO_FREEZE = 140;
@Override
public @NotNull Class<PlayerTickEvent> eventType() {
return PlayerTickEvent.class;
}
@Override
public @NotNull Result run(@NotNull PlayerTickEvent event) {
Player p = event.getPlayer();
Instance instance = p.getInstance();
if(instance == null) return Result.SUCCESS;
Pos pos = p.getPosition();
boolean inPowderSnow = instance.getBlock(pos).compare(Block.POWDER_SNOW)
|| instance.getBlock(pos.add(0, 1, 0)).compare(Block.POWDER_SNOW);
int ticksFrozen = p.getEntityMeta().getTickFrozen();
if(inPowderSnow) {
p.getEntityMeta().setTickFrozen(Math.min(TICKS_REQUIRED_TO_FREEZE, ticksFrozen + 1));
} else if(ticksFrozen > 0) {
p.getEntityMeta().setTickFrozen(Math.max(0, ticksFrozen - 2));
}
return Result.SUCCESS;
}
}
@@ -1,45 +1,66 @@
package eu.mhsl.minenet.minigames.instance; package eu.mhsl.minenet.minigames.instance;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.registry.RegistryKey;
import net.minestom.server.utils.NamespaceID; import net.minestom.server.registry.RegistryTag;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.world.DimensionType; import net.minestom.server.world.DimensionType;
import net.minestom.server.world.attribute.EnvironmentAttribute;
import net.minestom.server.world.clock.WorldClock;
import net.minestom.server.world.timeline.Timeline;
/** /**
* Prebuilt dimensions * Prebuilt dimensions
*/ */
public enum Dimension { public enum Dimension {
OVERWORLD( OVERWORLD(
NamespaceID.from("minenet:fullbright_overworld"), Key.key("minenet:fullbright_overworld"),
DimensionType DimensionType
.builder() .builder()
.ambientLight(2.0f) // Fullbright: seit MC 26.1 laeuft die Grundhelligkeit ueber das Environment-Attribut
// AMBIENT_LIGHT_COLOR (Vanilla-Overworld: #0A0A0A); der alte ambientLight-2.0-Hack
// liegt ausserhalb der validen Range und bricht das Client-Rendering
.setAttribute(EnvironmentAttribute.AMBIENT_LIGHT_COLOR, NamedTextColor.WHITE)
// defaultClock noetig, damit Instance#setTime/#defaultClock funktionieren (sonst null); Timelines wie Vanilla-Overworld
.defaultClock(WorldClock.OVERWORLD)
.timelines(RegistryTag.direct(Timeline.DAY, Timeline.MOON, Timeline.EARLY_GAME, Timeline.VILLAGER_SCHEDULE))
.build() .build()
), ),
NETHER( NETHER(
NamespaceID.from("minenet:fullbright_nether"), Key.key("minenet:fullbright_nether"),
DimensionType DimensionType
.builder() .builder()
.ambientLight(2.0f) // Fullbright: seit MC 26.1 laeuft die Grundhelligkeit ueber das Environment-Attribut
.effects("minecraft:the_nether") // AMBIENT_LIGHT_COLOR (Vanilla-Overworld: #0A0A0A); der alte ambientLight-2.0-Hack
// liegt ausserhalb der validen Range und bricht das Client-Rendering
.setAttribute(EnvironmentAttribute.AMBIENT_LIGHT_COLOR, NamedTextColor.WHITE)
.skybox(DimensionType.Skybox.NONE)
.defaultClock(WorldClock.OVERWORLD)
.timelines(RegistryTag.direct(Timeline.VILLAGER_SCHEDULE))
.build() .build()
), ),
THE_END( THE_END(
NamespaceID.from("minenet:fullbright_end"), Key.key("minenet:fullbright_end"),
DimensionType DimensionType
.builder() .builder()
.ambientLight(2.0f) // Fullbright: seit MC 26.1 laeuft die Grundhelligkeit ueber das Environment-Attribut
.effects("minecraft:the_end") // AMBIENT_LIGHT_COLOR (Vanilla-Overworld: #0A0A0A); der alte ambientLight-2.0-Hack
// liegt ausserhalb der validen Range und bricht das Client-Rendering
.setAttribute(EnvironmentAttribute.AMBIENT_LIGHT_COLOR, NamedTextColor.WHITE)
.skybox(DimensionType.Skybox.END)
.defaultClock(WorldClock.THE_END)
.timelines(RegistryTag.direct(Timeline.VILLAGER_SCHEDULE))
.build() .build()
); );
public final DimensionType DIMENSION; public final DimensionType DIMENSION;
public final NamespaceID namespaceID; public final Key namespaceID;
public final DynamicRegistry.Key<DimensionType> key; public final RegistryKey<DimensionType> key;
Dimension(NamespaceID namespaceID, DimensionType dimType) { Dimension(Key namespaceID, DimensionType dimType) {
this.DIMENSION = dimType; this.DIMENSION = dimType;
this.namespaceID = namespaceID; this.namespaceID = namespaceID;
@@ -1,21 +1,29 @@
package eu.mhsl.minenet.minigames.instance; package eu.mhsl.minenet.minigames.instance;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent; import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.instance.AddEntityToInstanceEvent; import net.minestom.server.event.instance.AddEntityToInstanceEvent;
import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent; import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent;
import net.minestom.server.instance.Instance; import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.registry.RegistryKey;
import net.minestom.server.timer.TaskSchedule; import net.minestom.server.timer.TaskSchedule;
import net.minestom.server.world.DimensionType; import net.minestom.server.world.DimensionType;
import java.util.UUID; import java.util.UUID;
public class MineNetInstance extends InstanceContainer { public class MineNetInstance extends InstanceContainer {
public MineNetInstance(DynamicRegistry.Key<DimensionType> type) { public MineNetInstance(RegistryKey<DimensionType> type) {
super(UUID.randomUUID(), type); this(UUID.randomUUID(), type);
}
// Eindeutiger Dimension-Name pro Instanz: Minestom entscheidet anhand dieses Namens (nicht des
// DimensionTypes), ob beim setInstance das volle Respawn-Protokoll gefahren wird. Ohne den Reset
// rendert der 26.x-Client nach einem Wechsel zwischen Instanzen gleicher Dimension keine Chunks.
private MineNetInstance(UUID uuid, RegistryKey<DimensionType> type) {
super(uuid, type, Key.key("minenet", "instance-" + uuid));
MinecraftServer.getInstanceManager().registerInstance(this); MinecraftServer.getInstanceManager().registerInstance(this);
this.eventNode() this.eventNode()
@@ -16,7 +16,7 @@ import net.minestom.server.event.item.ItemDropEvent;
import net.minestom.server.event.player.PlayerBlockBreakEvent; import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent; import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.event.player.PlayerMoveEvent; import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.registry.RegistryKey;
import net.minestom.server.timer.ExecutionType; import net.minestom.server.timer.ExecutionType;
import net.minestom.server.timer.TaskSchedule; import net.minestom.server.timer.TaskSchedule;
import net.minestom.server.world.DimensionType; import net.minestom.server.world.DimensionType;
@@ -34,7 +34,7 @@ public abstract class Game extends MineNetInstance implements Spawnable {
protected boolean isRunning = false; protected boolean isRunning = false;
protected boolean isBeforeBeginning = true; protected boolean isBeforeBeginning = true;
public Game(DynamicRegistry.Key<DimensionType> dimensionType) { public Game(RegistryKey<DimensionType> dimensionType) {
super(dimensionType); super(dimensionType);
MinecraftServer.getInstanceManager().registerInstance(this); MinecraftServer.getInstanceManager().registerInstance(this);
@@ -5,15 +5,19 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain.AcidRain
import eu.mhsl.minenet.minigames.instance.game.stateless.types.anvilRun.AnvilRunFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.anvilRun.AnvilRunFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.backrooms.BackroomsFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.backrooms.BackroomsFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.bedwars.BedwarsFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.bedwars.BedwarsFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBattle.BlockBattleFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace.BlockBreakRaceFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBreakRace.BlockBreakRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.boatRace.BoatRaceFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.boatRace.BoatRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.bowSpleef.BowSpleefFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.bowSpleef.BowSpleefFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.colorJump.ColorJumpFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.deathcube.DeathcubeFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.deathcube.DeathcubeFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.elytraRace.ElytraRaceFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.elytraRace.ElytraRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.fastbridge.FastbridgeFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.fastbridge.FastbridgeFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.highGround.HighGroundFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.highGround.HighGroundFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.jumpDive.JumpDiveFactory; 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.minerun.MinerunFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.pillars.PillarsFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.PlayerRaceFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.spaceSnake.SpaceSnakeFactory; import eu.mhsl.minenet.minigames.instance.game.stateless.types.spaceSnake.SpaceSnakeFactory;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.spleef.SpleefFactory; 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.stickfight.StickFightFactory;
@@ -46,7 +50,11 @@ public enum GameList {
FASTBRIDGE(new FastbridgeFactory(), GameType.OTHER), FASTBRIDGE(new FastbridgeFactory(), GameType.OTHER),
BLOCKBREAKRACE(new BlockBreakRaceFactory(), GameType.OTHER), BLOCKBREAKRACE(new BlockBreakRaceFactory(), GameType.OTHER),
SPACESNAKE(new SpaceSnakeFactory(), GameType.PVP), SPACESNAKE(new SpaceSnakeFactory(), GameType.PVP),
BOATRACE(new BoatRaceFactory(), GameType.OTHER); BOATRACE(new BoatRaceFactory(), GameType.OTHER),
PILLARS(new PillarsFactory(), GameType.PROTOTYPE),
BLOCKBATTLE(new BlockBattleFactory(), GameType.PVP),
COLORJUMP(new ColorJumpFactory(), GameType.JUMPNRUN),
PLAYERRACE(new PlayerRaceFactory(), GameType.JUMPNRUN);
private final GameFactory factory; private final GameFactory factory;
private final GameType type; private final GameType type;
@@ -9,7 +9,7 @@ import eu.mhsl.minenet.minigames.score.Score;
import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.registry.RegistryKey;
import net.minestom.server.timer.ExecutionType; import net.minestom.server.timer.ExecutionType;
import net.minestom.server.timer.Task; import net.minestom.server.timer.Task;
import net.minestom.server.timer.TaskSchedule; import net.minestom.server.timer.TaskSchedule;
@@ -26,7 +26,7 @@ public class StatelessGame extends Game {
private int timeLimit = 0; private int timeLimit = 0;
private int timePlayed = 0; private int timePlayed = 0;
public StatelessGame(DynamicRegistry.Key<DimensionType> dimensionType, String gameName, Score score) { public StatelessGame(RegistryKey<DimensionType> dimensionType, String gameName, Score score) {
super(dimensionType); super(dimensionType);
this.score = score; this.score = score;
this.name = gameName; this.name = gameName;
@@ -14,9 +14,9 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.instance.AddEntityToInstanceEvent; import net.minestom.server.event.instance.AddEntityToInstanceEvent;
import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent; import net.minestom.server.event.instance.RemoveEntityFromInstanceEvent;
import net.minestom.server.event.inventory.InventoryPreClickEvent;
import net.minestom.server.inventory.InventoryType; import net.minestom.server.inventory.InventoryType;
import net.minestom.server.inventory.click.ClickType; import net.minestom.server.inventory.click.Click;
import net.minestom.server.inventory.condition.InventoryConditionResult;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import net.minestom.server.sound.SoundEvent; import net.minestom.server.sound.SoundEvent;
@@ -106,8 +106,8 @@ public class GameConfigurationInventory extends InteractableInventory {
} }
@Override @Override
protected void onClick(Player player, int slot, ClickType clickType, InventoryConditionResult inventoryConditionResult) { protected void onClick(Player player, int slot, Click click, InventoryPreClickEvent event) {
inventoryConditionResult.setCancel(true); event.setCancelled(true);
if(!this.map.containsKey(slot)) return; if(!this.map.containsKey(slot)) return;
@@ -1,6 +1,7 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain; package eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain;
import de.articdive.jnoise.JNoise; import de.articdive.jnoise.generators.noisegen.opensimplex.FastSimplexNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.LastWinsScore; import eu.mhsl.minenet.minigames.score.LastWinsScore;
@@ -28,9 +29,8 @@ import java.util.concurrent.CompletableFuture;
public class AcidRain extends StatelessGame { public class AcidRain extends StatelessGame {
final JNoise noise = JNoise.newBuilder() final JNoise noise = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.09)
.setFrequency(0.09)
.build(); .build();
final private int radius = 20; final private int radius = 20;
final private int roofHeight = 55; final private int roofHeight = 55;
@@ -105,7 +105,7 @@ public class AcidRain extends StatelessGame {
for(int z = -radius; z <= radius; z++) { for(int z = -radius; z <= radius; z++) {
batch.setBlock(x, this.roofHeight, z, Block.AIR); batch.setBlock(x, this.roofHeight, z, Block.AIR);
if(new Pos(x, 0, z).distance(new Pos(0, 0, 0)) > radius) continue; if(new Pos(x, 0, z).distance(new Pos(0, 0, 0)) > radius) continue;
if(this.noise.getNoise(x + offset, z + offset) > 0.4) continue; if(this.noise.evaluateNoise(x + offset, z + offset) > 0.4) continue;
batch.setBlock(x, this.roofHeight, z, Block.OAK_PLANKS); batch.setBlock(x, this.roofHeight, z, Block.OAK_PLANKS);
} }
@@ -0,0 +1,207 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBattle;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.FirstWinsScore;
import eu.mhsl.minenet.minigames.util.BatchUtil;
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.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
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 org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class BlockBattle extends StatelessGame {
private final Team teamBlue = new Team(new Pos(0,101,20).add(0.5).withView(180, 0), Team.Color.BLUE);
private final Team teamRed = new Team(new Pos(0, 101, -20).add(0.5), Team.Color.RED);
private final int itemCount;
private final Map<Player, Team> teams = new WeakHashMap<>();
public BlockBattle(int itemCount) {
super(Dimension.THE_END.key, "Block Battle", new FirstWinsScore());
this.itemCount = itemCount;
this.eventNode().addChild(
CombatFeatures.empty()
.add(CombatFeatures.VANILLA_ATTACK)
.add(CombatFeatures.VANILLA_DAMAGE)
.add(CombatFeatures.VANILLA_KNOCKBACK)
.build().createNode()
);
this.eventNode().addListener(FinalAttackEvent.class, finalAttackEvent -> {
if(this.isBeforeBeginning) finalAttackEvent.setCancelled(true);
finalAttackEvent.setBaseDamage(0);
((Player) finalAttackEvent.getTarget()).setHealth(20);
});
}
@Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) {
this.generatePlatform(new Pos(0, 100, 0), Block.GOLD_BLOCK, Block.YELLOW_CONCRETE_POWDER);
this.generatePlatform(new Pos(0, 101, 0), Block.AIR, Block.SANDSTONE_SLAB);
this.generatePlatform(new Pos(0, 100, 20), Block.BLUE_CONCRETE, Block.BLUE_CONCRETE_POWDER);
this.generatePlatform(new Pos(0, 100, -20), Block.RED_CONCRETE, Block.RED_CONCRETE_POWDER);
this.generatePlatform(new Pos(-5, 101, -14), Block.RED_STAINED_GLASS, Block.AIR);
this.generatePlatform(new Pos(5, 101, 14), Block.BLUE_STAINED_GLASS, Block.AIR);
AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
Pos[] positionsRedGlass = {
new Pos(2, 102, -9),
new Pos(-1, 103, -9),
new Pos(-2, 104, -6),
new Pos(-5, 103, -7),
new Pos(-7, 102, -10),
new Pos(3, 102, -12),
new Pos(5, 101, -15)
};
Pos[] positionsBlueGlass = {
new Pos(-5, 101, 15),
new Pos(-3, 102, 12),
new Pos(-2, 102, 9),
new Pos(1, 103, 9),
new Pos(2, 104, 6),
new Pos(5, 103, 7),
new Pos(7, 102, 10)
};
for(Pos pos : positionsRedGlass)
batch.setBlock(pos, Block.RED_STAINED_GLASS);
for(Pos pos : positionsBlueGlass)
batch.setBlock(pos, Block.BLUE_STAINED_GLASS);
BatchUtil.loadAndApplyBatch(batch, this, () -> {});
}
@Override
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent playerBlockPlaceEvent) {
Pos posGoldBlock = new Pos(playerBlockPlaceEvent.getBlockPosition()).sub(0, 1, 0);
Block goldBlock = playerBlockPlaceEvent.getInstance().getBlock(posGoldBlock);
playerBlockPlaceEvent.setCancelled(goldBlock != Block.GOLD_BLOCK);
playerBlockPlaceEvent.getInstance().scheduler().scheduleNextTick(() -> {
Pos middle = new Pos(0, 101, 0);
boolean validBlue = true;
boolean validRed = true;
for(int x = middle.blockX()-1; x < middle.blockX()+2; x++) {
for(int z = middle.blockZ()-1; z < middle.blockZ()+2; z++) {
if(playerBlockPlaceEvent.getInstance().getBlock(x, 101, z) != Block.BLUE_WOOL) {
validBlue = false;
break;
}
}
if(!validBlue)
break;
}
for(int x = middle.blockX()-1; x < middle.blockX()+2; x++) {
for(int z = middle.blockZ()-1; z < middle.blockZ()+2; z++) {
if(playerBlockPlaceEvent.getInstance().getBlock(x, 101, z) != Block.RED_WOOL) {
validRed = false;
break;
}
}
if(!validRed)
break;
}
if(!validBlue && !validRed) return;
var winningTeam = validBlue ? Team.Color.BLUE : Team.Color.RED;
var winningPlayers = this.teams.entrySet().stream()
.filter(entry -> entry.getValue().color().equals(winningTeam))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
this.getScore().insertMultiple(winningPlayers);
super.stop();
});
}
@Override
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent playerBlockBreakEvent) {
boolean isAllowed = Arrays.stream(Team.Color.values())
.map(Team.Color::getMaterial)
.map(Material::block)
.toList()
.contains(playerBlockBreakEvent.getBlock());
if(!isAllowed) playerBlockBreakEvent.setCancelled(true);
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
if(playerMoveEvent.getNewPosition().y() < 95) {
var player = playerMoveEvent.getPlayer();
player.teleport(
this.isBeforeBeginning
? this.getSpawn()
: this.teams.get(player).spawnPosition()
);
this.giveItems(player);
}
}
@Override
protected void onStart() {
this.setTeams();
this.getPlayers().forEach(player -> player.teleport(this.teams.get(player).spawnPosition()).thenRun(() -> {
this.giveItems(player);
player.setGameMode(GameMode.SURVIVAL);
}));
}
private void generatePlatform(Pos center, Block inner, Block outer) {
for(int x = center.blockX()-2; x < center.blockX()+3; x++) {
for(int z = center.blockZ()-2; z < center.blockZ()+3; z++) {
this.setBlock(x, center.blockY(), z, outer);
}
}
for(int x = center.blockX()-1; x < center.blockX()+2; x++) {
for(int z = center.blockZ()-1; z < center.blockZ()+2; z++) {
this.setBlock(x, center.blockY(), z, inner);
}
}
}
private void setTeams() {
List<Player> players = new ArrayList<>(this.getPlayers());
Collections.shuffle(players);
int halfPlayers = players.size()/2;
players.subList(0, halfPlayers).forEach(player -> this.teams.put(player, this.teamBlue));
players.subList(halfPlayers, players.size()).forEach(player -> this.teams.put(player, this.teamRed));
}
@Override
public Pos getSpawn() {
return new Pos(0, 101, 0).add(0.5);
}
private void giveItems(Player player) {
player.getInventory().clear();
ItemStack item = ItemStack.of(
this.teams.get(player).color().getMaterial(),
this.itemCount
);
player.getInventory().addItemStack(item);
}
}
@@ -0,0 +1,40 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBattle;
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 BlockBattleFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_BlockBattle#name");
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_BlockBattle#description");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new NumericOption("itemCount", Material.WHITE_WOOL, TranslatedComponent.byId("game_BlockBattle#itemCount"), 1, 2, 3));
}
@Override
public Material symbol() {
return Material.GREEN_CONCRETE;
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new BlockBattle(configuration.get("itemCount").getAsInt()).setParent(parent);
}
}
@@ -0,0 +1,21 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.blockBattle;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.item.Material;
public record Team(Pos spawnPosition, Color color) {
public enum Color {
RED(Material.RED_WOOL),
BLUE(Material.BLUE_WOOL);
private final Material block;
Color(Material block) {
this.block = block;
}
public Material getMaterial() {
return this.block;
}
}
}
@@ -0,0 +1,176 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.colorJump;
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 net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
import net.minestom.server.event.inventory.InventoryPreClickEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.event.player.PlayerSwapItemEvent;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack;
import net.minestom.server.timer.Scheduler;
import net.minestom.server.timer.TaskSchedule;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
public class ColorJump extends StatelessGame {
private final List<Block> blocks = List.of(
Block.RED_CONCRETE,
Block.ORANGE_CONCRETE,
Block.YELLOW_CONCRETE,
Block.LIME_CONCRETE,
Block.LIGHT_BLUE_CONCRETE,
Block.BLUE_CONCRETE,
Block.PURPLE_CONCRETE,
Block.PINK_CONCRETE
);
private Block currentBlock;
private Block blockLastRound;
private double roundTime = 5;
private final double multiplierNextRoundTime = 0.9;
public ColorJump() {
super(Dimension.THE_END.key, "ColorJump", new LastWinsScore());
this.getScore().setIgnoreLastPlayers(1);
this.setGenerator(new CircularPlateTerrainGenerator(100));
this.eventNode().addListener(
InventoryPreClickEvent.class,
inventoryPreClickEvent -> inventoryPreClickEvent.setCancelled(true)
);
this.eventNode().addListener(
PlayerSwapItemEvent.class,
playerSwapItemEvent -> playerSwapItemEvent.setCancelled(true)
);
}
@Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) {
this.generate();
}
@Override
protected void onStart() {
this.nextRound();
}
private void nextRound() {
this.generate();
do {
this.currentBlock = this.blocks.get(ThreadLocalRandom.current().nextInt(this.blocks.size()));
} while(this.currentBlock == this.blockLastRound);
this.blockLastRound = this.currentBlock;
ItemStack item = ItemStack.of(Objects.requireNonNull(this.currentBlock.registry().material()));
this.getPlayers().forEach(player -> {
player.getInventory().clear();
for(int i = 0; i < 9; i++) {
player.getInventory().setItemStack(i, item);
}
});
var scheduler = MinecraftServer.getSchedulerManager();
TaskSchedule stop = TaskSchedule.stop();
scheduler.scheduleTask(() -> {
if(!this.isRunning) return stop;
this.destroyAllExcept(this.currentBlock);
scheduler.scheduleTask(() -> {
if(this.isRunning) this.nextRound();
return stop;
}, TaskSchedule.seconds(3));
return stop;
}, TaskSchedule.seconds((long) this.roundTime));
this.roundTime = this.roundTime * this.multiplierNextRoundTime;
if(this.roundTime <= 1) this.roundTime = 1;
long totalTicks = (long) (this.roundTime * 20);
this.scheduleExpUpdate(scheduler, totalTicks, totalTicks);
}
private void scheduleExpUpdate(Scheduler scheduler, long remainingTicks, long totalTicks) {
if (!this.isRunning) return;
if (remainingTicks <= 0) {
this.getPlayers().forEach(player -> {
player.setExp(0f);
player.setLevel(0);
});
return;
}
float progress = (float) remainingTicks / totalTicks;
this.getPlayers().forEach(player -> {
player.setExp(progress);
player.setLevel((int) Math.ceil(remainingTicks / 20.0));
});
scheduler.scheduleTask(() -> {
this.scheduleExpUpdate(scheduler, remainingTicks -1, totalTicks);
return TaskSchedule.stop();
}, TaskSchedule.tick(1));
}
private void generate() {
int y = 30;
AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
for (int x = -20; x <= 21; x += 2) {
for (int z = -11; z <= 10; z += 2) {
Block randomBlock = this.blocks.get(ThreadLocalRandom.current().nextInt(this.blocks.size()));
for (int dx = 0; dx < 2; dx++) {
for (int dz = 0; dz < 2; dz++) {
batch.setBlock(x + dx, y, z + dz, randomBlock);
}
}
}
}
BatchUtil.loadAndApplyBatch(batch, this, () -> {});
}
private void destroyAllExcept(Block block) {
AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
int y = 30;
for (int x = -20; x <= 21; x++) {
for(int z = -11; z <= 10; z++) {
Pos blockPos = new Pos(x, y, z);
if(this.getBlock(blockPos) != block)
batch.setBlock(blockPos, Block.AIR);
}
}
BatchUtil.loadAndApplyBatch(batch, this, () -> {});
}
@Override
public Pos getSpawn() {
return new Pos(0, 31, 0);
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
var player = playerMoveEvent.getPlayer();
if(playerMoveEvent.getNewPosition().y() >= 29)
return;
player.teleport(this.getSpawn());
if(this.isRunning) {
this.getScore().insertResult(player);
player.setGameMode(GameMode.SPECTATOR);
}
}
}
@@ -0,0 +1,33 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.colorJump;
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.room.Room;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.minestom.server.item.Material;
import java.util.Map;
public class ColorJumpFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_ColorJump#name");
}
@Override
public Material symbol() {
return Material.PINK_CONCRETE;
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_ColorJump#description");
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new ColorJump().setParent(parent);
}
}
@@ -24,6 +24,7 @@ import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.event.player.PlayerStartFlyingWithElytraEvent; import net.minestom.server.event.player.PlayerStartFlyingWithElytraEvent;
import net.minestom.server.event.player.PlayerStopFlyingWithElytraEvent; import net.minestom.server.event.player.PlayerStopFlyingWithElytraEvent;
import net.minestom.server.event.player.PlayerUseItemEvent; import net.minestom.server.event.player.PlayerUseItemEvent;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.batch.AbsoluteBlockBatch; import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
@@ -209,7 +210,16 @@ public class ElytraRace extends StatelessGame {
point -> batch.setBlock(point, Block.AIR) point -> batch.setBlock(point, Block.AIR)
); );
int minChunkX = (ringPos.blockX() - 100) >> 4;
int maxChunkX = (ringPos.blockX() + 100) >> 4;
int chunkZ = ringPos.blockZ() >> 4;
BatchUtil.loadAndApplyBatch(batch, this, () -> { BatchUtil.loadAndApplyBatch(batch, this, () -> {
for(int cx = minChunkX; cx <= maxChunkX; cx++) {
Chunk chunk = this.getChunk(cx, chunkZ);
if(chunk == null) continue;
this.getPlayers().forEach(chunk::sendChunk);
}
}); });
} }
@@ -229,7 +239,7 @@ public class ElytraRace extends StatelessGame {
if(data.currentCheckpoint == 0) checkpointPos = this.getSpawn().add(0, 3, 0); if(data.currentCheckpoint == 0) checkpointPos = this.getSpawn().add(0, 3, 0);
p.setVelocity(Vec.ZERO); p.setVelocity(Vec.ZERO);
p.setFlyingWithElytra(false); p.setFlyingWithElytra(false);
p.teleport(Pos.fromPoint(checkpointPos).add(0.5, 0, 0.5)); p.teleport(new Pos(checkpointPos).add(0.5, 0, 0.5));
p.setFlying(true); p.setFlying(true);
p.setFlyingSpeed(0); p.setFlyingSpeed(0);
@@ -8,6 +8,7 @@ import io.github.togar2.pvp.events.EntityKnockbackEvent;
import io.github.togar2.pvp.events.FinalAttackEvent; import io.github.togar2.pvp.events.FinalAttackEvent;
import io.github.togar2.pvp.events.PrepareAttackEvent; import io.github.togar2.pvp.events.PrepareAttackEvent;
import io.github.togar2.pvp.feature.CombatFeatures; import io.github.togar2.pvp.feature.CombatFeatures;
import io.github.togar2.pvp.feature.knockback.KnockbackSettings;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.instance.InstanceTickEvent; import net.minestom.server.event.instance.InstanceTickEvent;
@@ -50,7 +51,15 @@ class HighGround extends StatelessGame {
this.eventNode().addListener( this.eventNode().addListener(
EntityKnockbackEvent.class, EntityKnockbackEvent.class,
entityKnockbackEvent -> entityKnockbackEvent.setStrength(1.1f) entityKnockbackEvent -> entityKnockbackEvent.setSettings(
KnockbackSettings.builder()
.horizontal(1.1)
.vertical(KnockbackSettings.DEFAULT.vertical())
.verticalLimit(KnockbackSettings.DEFAULT.verticalLimit())
.extraHorizontal(KnockbackSettings.DEFAULT.extraHorizontal())
.extraVertical(KnockbackSettings.DEFAULT.extraVertical())
.build()
)
); );
this.eventNode().addListener(InstanceTickEvent.class, instanceTickEvent -> { this.eventNode().addListener(InstanceTickEvent.class, instanceTickEvent -> {
@@ -0,0 +1,143 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.pillars;
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.Position;
import io.github.togar2.pvp.feature.CombatFeatures;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.item.ItemDropEvent;
import net.minestom.server.event.player.PlayerBlockBreakEvent;
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.timer.TaskSchedule;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
class Pillars extends StatelessGame {
private int spawnPosx = 0;
private int spawnPosz = 0;
private final int pillarSpacing = 10;
private final int pillarRowCount = 5;
private final int timeToNextItem = 5;
private static final Set<String> IGNORED_SUFFIXES = Set.of(
"_SWORD", "_PICKAXE", "_AXE", "_SHOVEL", "_HOE",
"_HELMET", "_CHESTPLATE", "_LEGGINGS", "_BOOTS",
"_BOAT", "_MINECART", "_BUCKET", "_SPAWN_EGG",
"_BANNER", "_CANDLE", "_PRESSURE_PLATE", "_TEMPLATE",
"_BUTTON", "_HARNESS"
);
private static final Set<Material> IGNORED_EXACT = Set.of(
Material.BOW, Material.CROSSBOW, Material.TRIDENT, Material.MACE, Material.SHIELD,
Material.SHEARS, Material.FLINT_AND_STEEL, Material.FISHING_ROD, Material.SADDLE,
Material.TURTLE_HELMET, Material.ELYTRA,
Material.LADDER, Material.VINE, Material.SCAFFOLDING, Material.TWISTING_VINES, Material.WEEPING_VINES,
Material.BAMBOO_RAFT,
Material.TNT, Material.SPAWNER, Material.END_PORTAL_FRAME,
Material.COMMAND_BLOCK, Material.CHAIN_COMMAND_BLOCK, Material.REPEATING_COMMAND_BLOCK,
Material.STRUCTURE_BLOCK, Material.STRUCTURE_VOID, Material.JIGSAW,
Material.BARRIER, Material.LIGHT, Material.DEBUG_STICK, Material.KNOWLEDGE_BOOK,
Material.BEDROCK, Material.AIR
);
private static final Set<Material> IGNORED_MATERIALS = new HashSet<>();
static {
for (Material material : Material.values()) {
String id = material.key().asString();
for (String suffix : IGNORED_SUFFIXES) {
if (id.endsWith(suffix.toLowerCase())) {
IGNORED_MATERIALS.add(material);
break;
}
}
}
}
public Pillars() {
super(Dimension.THE_END.key, "Pillars", new LastWinsScore());
this.getScore().setIgnoreLastPlayers(1);
this.eventNode().addChild(
CombatFeatures.empty()
.add(CombatFeatures.VANILLA_ATTACK)
.add(CombatFeatures.VANILLA_DAMAGE)
.add(CombatFeatures.VANILLA_KNOCKBACK)
.build().createNode()
);
}
@Override
protected boolean onPlayerJoin(Player p) {
Pos pos = new Pos(this.spawnPosx * this.pillarSpacing, 100, this.spawnPosz * this.pillarSpacing);
this.setBlock(pos.sub(0, 1, 0), Block.BEDROCK);
if(this.spawnPosx >= this.pillarRowCount) {
this.spawnPosx = 0;
this.spawnPosz++;
}
this.spawnPosx++;
MinecraftServer.getSchedulerManager().scheduleNextTick(() -> p.teleport(pos.add(0.5, 0, 0.5)));
return super.onPlayerJoin(p);
}
@Override
protected void onBlockPlace(@NotNull PlayerBlockPlaceEvent playerBlockPlaceEvent) {}
@Override
protected void onBlockBreak(@NotNull PlayerBlockBreakEvent playerBlockBreakEvent) {}
@Override
protected void onItemDrop(@NotNull ItemDropEvent itemDropEvent) {}
@Override
public Pos getSpawn() {
return new Pos(0, 105, 0);
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
var player = playerMoveEvent.getPlayer();
if(this.isBeforeBeginning && Position.hasPositionChanged(player.getPosition(), playerMoveEvent.getNewPosition()))
playerMoveEvent.setCancelled(true);
if(playerMoveEvent.getNewPosition().y() < 80) {
this.getScore().insertResult(player);
player.teleport(this.getSpawn());
player.setGameMode(GameMode.SPECTATOR);
}
}
@Override
protected void onStart() {
this.getPlayers().forEach(player -> player.setGameMode(GameMode.SURVIVAL));
MinecraftServer.getSchedulerManager().submitTask(() -> {
List<Material> materials = Material.values().stream()
.filter(material -> !IGNORED_EXACT.contains(material))
.filter(material -> !IGNORED_MATERIALS.contains(material))
.toList();
this.getPlayers().forEach(player -> {
ItemStack item = ItemStack.of(materials.get(ThreadLocalRandom.current().nextInt(materials.size())));
player.getInventory().addItemStack(item);
});
return TaskSchedule.seconds(this.timeToNextItem);
});
}
}
@@ -0,0 +1,32 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.pillars;
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.room.Room;
import eu.mhsl.minenet.minigames.message.component.TranslatedComponent;
import net.minestom.server.item.Material;
import java.util.Map;
public class PillarsFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_Pillars#name");
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_Pillars#description");
}
@Override
public Material symbol() {
return Material.BEDROCK;
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new Pillars().setParent(parent);
}
}
@@ -0,0 +1,86 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.FirstWinsScore;
import eu.mhsl.minenet.minigames.util.BatchUtil;
import eu.mhsl.minenet.minigames.util.CommonProperties;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
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.Map;
import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;
public class PlayerRace extends StatelessGame {
private final int height = 64;
private final Map<Player, Double> zLine = new WeakHashMap<>();
private final Map<Player, Double> zSpawnPoint = new WeakHashMap<>();
private final int obstacleCount;
public PlayerRace(int obstacleCount) {
super(Dimension.THE_END.key, "PlayerRace", new FirstWinsScore());
this.setGenerator(new PlayerRaceChunkgenerator(this.height, obstacleCount));
this.obstacleCount = obstacleCount;
}
@Override
protected boolean onPlayerJoin(Player p) {
p.setGameMode(GameMode.ADVENTURE);
this.zLine.put(p, 32.0);
this.zSpawnPoint.put(p, 14.5);
return false;
}
@Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) {
AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
for(int x = 0; x < 16; x++) {
batch.setBlock(x,this.height + 1, 15, Block.OAK_FENCE.withProperties(CommonProperties.fenceEastWest));
}BatchUtil.loadAndApplyBatch(batch, this, () -> callback.complete(null));
}
@Override
protected void onStart() {
AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
for(int x = 0; x < 16; x++) {
batch.setBlock(x,this.height + 1, 15, Block.AIR);
}BatchUtil.loadAndApplyBatch(batch, this, () -> {});
}
@Override
public Pos getSpawn() {
return new Pos(8, this.height + 1.5, 8);
}
public Pos getNewSpawn(Player player) {
return new Pos(8, this.height + 1.5, this.zSpawnPoint.get(player));
}
@Override
protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
var player = playerMoveEvent.getPlayer();
if(playerMoveEvent.getNewPosition().y() < this.height - 5) {
player.teleport(this.isBeforeBeginning ? this.getSpawn() : this.getNewSpawn(player));
return;
}
if(playerMoveEvent.getNewPosition().z() >= this.zLine.get(player) && this.isRunning) {
this.zLine.merge(player, 19.0, Double::sum);
this.zSpawnPoint.merge(player, 19.0, Double::sum);
}
if(playerMoveEvent.getNewPosition().z() >= 16 + this.obstacleCount * 19 && this.isRunning) {
this.getScore().insertResult(player);
player.setGameMode(GameMode.SPECTATOR);
}
if(playerMoveEvent.getNewPosition().z() >= 16 && this.isBeforeBeginning)
player.teleport(this.getSpawn());
}
}
@@ -0,0 +1,61 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles.Obstacle;
import eu.mhsl.minenet.minigames.util.GeneratorUtils;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.featureEnriched.ValeGenerator;
import eu.mhsl.minenet.minigames.world.generator.terrain.BaseGenerator;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
public class PlayerRaceChunkgenerator extends BaseGenerator {
private static final int START_LENGTH = 16;
private static final int GAP_LENGTH = 3;
private static final int PERIOD = Obstacle.LENGTH + GAP_LENGTH;
public PlayerRaceChunkgenerator(int height, int obstacleCount) {
ValeGenerator vale = new ValeGenerator();
vale.setXShiftMultiplier(integer -> 0.5d);
vale.setHeightNoiseMultiplier(integer -> 2);
this.addMixIn(vale);
int finishStart = START_LENGTH + obstacleCount * PERIOD;
int finishEnd = finishStart + 16;
this.addMixIn(unit -> {
Point chunkStart = unit.absoluteStart();
if(
chunkStart.chunkX() != 0 ||
chunkStart.blockZ() + 16 <= 0 ||
chunkStart.blockZ() >= finishEnd
) return;
GeneratorUtils.foreachXZ(unit, point -> {
Block ground = groundBlockAt(point.blockZ(), finishStart, finishEnd);
if(ground != null) unit.modifier().setBlock(point.withY(height), ground);
});
for(int i = 0; i < obstacleCount; i++) {
int obstacleZ = START_LENGTH + i * PERIOD;
if(obstacleZ < chunkStart.blockZ() || obstacleZ >= chunkStart.blockZ() + 16) continue;
GenerationUnit fork = unit.fork(
new Vec(chunkStart.blockX(), height - 8, obstacleZ),
new Vec(chunkStart.blockX() + 16, height + 16, obstacleZ + Obstacle.LENGTH)
);
Obstacle.getRandomObstacle().generate(fork, new Vec(chunkStart.blockX(), height, obstacleZ));
}
});
}
private static Block groundBlockAt(int z, int finishStart, int finishEnd) {
if(z < 0 || z >= finishEnd) return null;
if(z < START_LENGTH) return BlockPallet.GROUND.rnd();
if(z >= finishStart) return Block.DIAMOND_BLOCK;
int relative = (z - START_LENGTH) % PERIOD;
return relative >= Obstacle.LENGTH ? BlockPallet.GROUND.rnd() : null;
}
}
@@ -0,0 +1,40 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace;
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 PlayerRaceFactory implements GameFactory {
@Override
public TranslatedComponent name() {
return TranslatedComponent.byId("game_PlayerRace#name");
}
@Override
public ConfigManager configuration() {
return new ConfigManager()
.addOption(new NumericOption("length", Material.RAIL, TranslatedComponent.byId("optionCommon#length"), 8, 12, 16, 20));
}
@Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) throws Exception {
return new PlayerRace(configuration.get("length").getAsInt()).setParent(parent);
}
@Override
public Material symbol() {
return Material.DIRT_PATH;
}
@Override
public TranslatedComponent description() {
return TranslatedComponent.byId("game_PlayerRace#description");
}
}
@@ -0,0 +1,30 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class BambooThicket extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
unit.modifier().setBlock(point.withY(start.blockY()), rnd.nextInt(3) == 0 ? Block.PODZOL : Block.GRASS_BLOCK);
int z = relZ(point, start);
if(z < 2 || z > 13 || rnd.nextInt(100) >= 45) return;
int height = 3 + rnd.nextInt(3);
for(int y = 1; y <= height; y++) {
String leaves = y == height ? "large" : y == height - 1 ? "small" : "none";
unit.modifier().setBlock(
point.withY(start.blockY() + y),
Block.BAMBOO.withProperty("age", "1").withProperty("leaves", leaves)
);
}
});
}
}
@@ -0,0 +1,44 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class BasaltPillars extends Obstacle {
private final JNoise ground = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.13)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
double noise = this.ground.evaluateNoise(point.blockX(), point.blockZ());
Block floor = noise > 0.4 ? Block.SOUL_SOIL : noise < -0.4 ? Block.BASALT : Block.BLACKSTONE;
unit.modifier().setBlock(point.withY(start.blockY()), floor);
});
for(int i = 0; i < 16; i++) {
int x = rnd.nextInt(16);
int z = 2 + rnd.nextInt(12);
int height = 2 + rnd.nextInt(3);
boolean thick = rnd.nextInt(3) == 0 && x < 15;
Block pillar = rnd.nextInt(3) == 0 ? Block.POLISHED_BASALT : Block.BASALT;
for(int y = 1; y <= height; y++) {
unit.modifier().setBlock(start.add(x, y, z), pillar);
if(thick) {
unit.modifier().setBlock(start.add(x + 1, y, z), pillar);
}
}
if(rnd.nextInt(3) == 0) {
unit.modifier().setBlock(start.add(x, height + 1, z), Block.SOUL_LANTERN);
}
}
}
}
@@ -0,0 +1,28 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class BerryPatch extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
int z = relZ(point, start);
int pathX = 7 + (int) Math.round(5 * Math.sin(z * Math.PI / 7.5));
boolean onPath = relX(point, start) == pathX || relX(point, start) == pathX + 1;
boolean overgrown = z == 8;
unit.modifier().setBlock(point.withY(start.blockY()), onPath ? Block.DIRT_PATH : Block.GRASS_BLOCK);
boolean bush = onPath ? overgrown : z >= 1 && z <= 14 && rnd.nextInt(100) < 45;
if(bush) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.SWEET_BERRY_BUSH.withProperty("age", "3"));
}
});
}
}
@@ -0,0 +1,40 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class BoulderTrench extends Obstacle {
private static final int[] DEPTHS = {0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0};
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
int depth = DEPTHS[relZ(point, start)];
for(int y = start.blockY() - 2; y < start.blockY() - depth; y++) {
unit.modifier().setBlock(point.withY(y), Block.STONE);
}
Block floor = depth == 0
? Block.GRASS_BLOCK
: rnd.nextBoolean() ? Block.COARSE_DIRT : Block.GRAVEL;
unit.modifier().setBlock(point.withY(start.blockY() - depth), floor);
});
for(int i = 0; i < 10; i++) {
int x = rnd.nextInt(16);
int z = 6 + rnd.nextInt(4);
unit.modifier().setBlock(start.add(x, -1, z), rnd.nextBoolean() ? Block.MOSSY_COBBLESTONE : Block.COBBLESTONE);
if(rnd.nextInt(3) == 0) {
unit.modifier().setBlock(start.add(x, 0, z), Block.MOSSY_COBBLESTONE);
}
}
for(int i = 0; i < 4; i++) {
unit.modifier().setBlock(start.add(rnd.nextInt(16), -1, 6 + rnd.nextInt(4)), Block.COBWEB);
}
}
}
@@ -0,0 +1,65 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class ChorusGrove extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
Block floor = rnd.nextInt(14) == 0 ? Block.PURPUR_BLOCK : Block.END_STONE;
unit.modifier().setBlock(point.withY(start.blockY()), floor);
});
int[] rows = {5, 10};
for(int i = 0; i < rows.length; i++) {
int gapStart = i % 2 == 0 ? 1 + rnd.nextInt(4) : 10 + rnd.nextInt(4);
boolean[] hasWall = new boolean[16];
for(int x = 0; x < 16; x++) {
hasWall[x] = x < gapStart || x >= gapStart + 2;
}
for(int x = 0; x < 16; x++) {
if(!hasWall[x]) continue;
boolean westConnected = x > 0 && hasWall[x - 1];
boolean eastConnected = x < 15 && hasWall[x + 1];
Block wall = Block.END_STONE_BRICK_WALL
.withProperty("west", westConnected ? "low" : "none")
.withProperty("east", eastConnected ? "low" : "none")
.withProperty("up", String.valueOf(!westConnected || !eastConnected || x % 4 == 0));
unit.modifier().setBlock(start.add(x, 1, rows[i]), wall);
if(rnd.nextInt(6) == 0) {
unit.modifier().setBlock(start.add(x, 2, rows[i]), Block.END_ROD);
}
}
}
for(int i = 0; i < 15; i++) {
int x = rnd.nextInt(16);
int z = 2 + rnd.nextInt(12);
if(z == 5 || z == 10) continue;
int height = 1 + rnd.nextInt(3);
for(int y = 1; y <= height; y++) {
unit.modifier().setBlock(start.add(x, y, z), Block.CHORUS_PLANT);
}
unit.modifier().setBlock(start.add(x, height + 1, z), Block.CHORUS_FLOWER.withProperty("age", "5"));
}
for(int i = 0; i < 5; i++) {
int x = rnd.nextInt(16);
int z = 2 + rnd.nextInt(12);
if(z == 5 || z == 10) continue;
unit.modifier().setBlock(start.add(x, 1, z), Block.PURPUR_BLOCK);
if(rnd.nextBoolean()) {
unit.modifier().setBlock(start.add(x, 2, z), Block.PURPUR_BLOCK);
}
}
}
}
@@ -0,0 +1,46 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class CobwebGrove extends Obstacle {
private final JNoise soil = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.12)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
Block floor = this.soil.evaluateNoise(point.blockX(), point.blockZ()) > 0 ? Block.PODZOL : Block.COARSE_DIRT;
unit.modifier().setBlock(point.withY(start.blockY()), floor);
int z = relZ(point, start);
if(z < 2 || z > 13) return;
int roll = rnd.nextInt(100);
if(roll < 28) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.COBWEB);
if(roll < 8) unit.modifier().setBlock(point.withY(start.blockY() + 2), Block.COBWEB);
} else if(roll < 36) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.FERN);
}
});
for(int i = 0; i < 4; i++) {
int x = rnd.nextInt(16);
int z = 3 + rnd.nextInt(10);
int height = 1 + rnd.nextInt(2);
for(int y = 1; y <= height; y++) {
unit.modifier().setBlock(start.add(x, y, z), Block.SPRUCE_LOG);
}
}
}
}
@@ -0,0 +1,46 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class DripstoneSpikes extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point ->
unit.modifier().setBlock(point.withY(start.blockY()), rnd.nextInt(6) == 0 ? Block.STONE : Block.DRIPSTONE_BLOCK));
int[] rows = {4, 8, 12};
for(int i = 0; i < rows.length; i++) {
int gapStart = i % 2 == 0 ? 2 + rnd.nextInt(4) : 9 + rnd.nextInt(4);
for(int x = 0; x < 16; x++) {
if(x >= gapStart && x < gapStart + 2) continue;
unit.modifier().setBlock(start.add(x, 1, rows[i]), Block.DRIPSTONE_BLOCK);
this.placeSpike(unit, start.add(0, 1, 0), x, rows[i], 1 + rnd.nextInt(2));
}
}
for(int i = 0; i < 12; i++) {
int x = rnd.nextInt(16);
int z = 2 + rnd.nextInt(12);
if(z == 4 || z == 8 || z == 12) continue;
this.placeSpike(unit, start, x, z, 1 + rnd.nextInt(3));
}
}
private void placeSpike(GenerationUnit unit, Point start, int x, int z, int height) {
for(int y = 1; y <= height; y++) {
String thickness = y == height ? "tip" : y == height - 1 ? "frustum" : "base";
unit.modifier().setBlock(
start.add(x, y, z),
Block.POINTED_DRIPSTONE
.withProperty("thickness", thickness)
.withProperty("vertical_direction", "up")
);
}
}
}
@@ -0,0 +1,36 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class GlassPaneWeave extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point ->
unit.modifier().setBlock(point.withY(start.blockY()), rnd.nextInt(5) == 0 ? Block.CALCITE : Block.QUARTZ_BLOCK));
int[] rows = {4, 8, 12};
for(int i = 0; i < rows.length; i++) {
int gapStart = i % 2 == 0 ? 2 + rnd.nextInt(3) : 11 + rnd.nextInt(3);
boolean[] hasPane = new boolean[16];
for(int x = 0; x < 16; x++) {
hasPane[x] = x < gapStart || x >= gapStart + 2;
}
for(int x = 0; x < 16; x++) {
if(!hasPane[x]) continue;
Block pane = (rnd.nextInt(6) == 0 ? Block.GRAY_STAINED_GLASS_PANE : Block.WHITE_STAINED_GLASS_PANE)
.withProperty("west", String.valueOf(x > 0 && hasPane[x - 1]))
.withProperty("east", String.valueOf(x < 15 && hasPane[x + 1]));
unit.modifier().setBlock(start.add(x, 1, rows[i]), pane);
unit.modifier().setBlock(start.add(x, 2, rows[i]), pane);
}
}
}
}
@@ -0,0 +1,35 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class HayHurdles extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
unit.modifier().setBlock(point.withY(start.blockY()), Block.GRASS_BLOCK);
if(relZ(point, start) % 3 != 0 && rnd.nextInt(8) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.SHORT_GRASS);
}
});
int[] rows = {3, 6, 9, 12};
for(int i = 0; i < rows.length; i++) {
boolean tall = i % 2 == 1;
int gapStart = rnd.nextInt(14);
for(int x = 0; x < 16; x++) {
if(tall && (x == gapStart || x == gapStart + 1)) continue;
Block block = rnd.nextInt(12) == 0 ? Block.CARVED_PUMPKIN : Block.HAY_BLOCK;
unit.modifier().setBlock(start.add(x, 1, rows[i]), block);
if(tall) {
unit.modifier().setBlock(start.add(x, 2, rows[i]), Block.HAY_BLOCK);
}
}
}
}
}
@@ -0,0 +1,35 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class HoneyField extends Obstacle {
private final JNoise sticky = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.2)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
boolean stickyFloor = this.sticky.evaluateNoise(point.blockX(), point.blockZ()) > 0.35;
unit.modifier().setBlock(point.withY(start.blockY()), stickyFloor ? Block.HONEY_BLOCK : Block.HONEYCOMB_BLOCK);
});
for(int wallZ : new int[]{5, 10}) {
int gapStart = wallZ == 5 ? 1 + rnd.nextInt(5) : 8 + rnd.nextInt(5);
for(int x = 0; x < 16; x++) {
if(x >= gapStart && x < gapStart + 2) continue;
unit.modifier().setBlock(start.add(x, 1, wallZ), Block.HONEY_BLOCK);
unit.modifier().setBlock(start.add(x, 2, wallZ), Block.HONEY_BLOCK);
}
}
}
}
@@ -0,0 +1,54 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class IceRink extends Obstacle {
private final JNoise streaks = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.15)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
double noise = this.streaks.evaluateNoise(point.blockX(), point.blockZ());
Block floor = noise > 0.45 ? Block.BLUE_ICE : noise < -0.55 ? Block.ICE : Block.PACKED_ICE;
unit.modifier().setBlock(point.withY(start.blockY()), floor);
if(relZ(point, start) >= 2 && relZ(point, start) <= 13 && rnd.nextInt(14) == 0) {
unit.modifier().setBlock(
point.withY(start.blockY() + 1),
Block.SNOW.withProperty("layers", String.valueOf(1 + rnd.nextInt(2)))
);
}
});
for(int wallZ : new int[]{5, 10}) {
int gapStart = wallZ == 5 ? 1 + rnd.nextInt(4) : 9 + rnd.nextInt(4);
for(int x = 0; x < 16; x++) {
if(x >= gapStart && x < gapStart + 3) continue;
unit.modifier().setBlock(start.add(x, 1, wallZ), Block.PACKED_ICE);
unit.modifier().setBlock(start.add(x, 2, wallZ), Block.ICE);
if(rnd.nextInt(4) == 0) {
unit.modifier().setBlock(start.add(x, 3, wallZ), Block.SNOW.withProperty("layers", "1"));
}
}
}
for(int i = 0; i < 5; i++) {
int x = rnd.nextInt(16);
int z = 2 + rnd.nextInt(12);
if(z == 5 || z == 10) continue;
unit.modifier().setBlock(start.add(x, 1, z), Block.PACKED_ICE);
unit.modifier().setBlock(start.add(x, 2, z), Block.SNOW.withProperty("layers", "1"));
}
}
}
@@ -0,0 +1,36 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class LadderWall extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point ->
unit.modifier().setBlock(point.withY(start.blockY()), BlockPallet.STONE.rnd()));
for(int x = 0; x < 16; x++) {
boolean hasLadder = rnd.nextInt(3) != 0;
for(int y = 1; y <= 3; y++) {
for(int z = 7; z <= 8; z++) {
Block block = rnd.nextInt(3) == 0 ? Block.MOSSY_STONE_BRICKS : Block.STONE_BRICKS;
unit.modifier().setBlock(start.add(x, y, z), block);
}
if(hasLadder) {
unit.modifier().setBlock(start.add(x, y, 6), Block.LADDER.withProperty("facing", "north"));
}
unit.modifier().setBlock(start.add(x, y, 9), Block.LADDER.withProperty("facing", "south"));
}
if(x % 5 == 2) {
unit.modifier().setBlock(start.add(x, 4, 7), Block.LANTERN);
}
}
}
}
@@ -0,0 +1,48 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.*;
class LadderWall2 extends Obstacle{
@Override
public void generate(GenerationUnit unit, Point start) {
forEachColumn(start, point ->
unit.modifier().setBlock(point.withY(start.blockY()), BlockPallet.STREET.rnd()));
this.buildWall(unit, start,4, 4, false);
this.buildWall(unit, start,10, 8, true);
}
private void buildWall(GenerationUnit unit, Point start, int z, int height, boolean lastRow) {
Random random = new Random();
int y = 1;
for(int x = 0; x < 16; x++) {
for(int wy = 0; wy <= height; wy++) {
unit.modifier().setBlock(start.add(x, y + wy, z), BlockPallet.STONE.rnd());
}
}
for(int wy = lastRow ? 2 : 0; wy <= height; wy++) {
Set<Integer> noLadder = new HashSet<>();
for(int i = 0; i < 5; i++) {
noLadder.add(random.nextInt(16));
}
for(int x = 0; x < 16; x++) {
if(!noLadder.contains(x))
unit.modifier().setBlock(start.add(x, y + wy, z - 1), Block.LADDER);
}
if(!lastRow) {
for(int x = 7; x < 9; x++) {
unit.modifier().setBlock(start.add(x, y + wy, z + 1), Block.LADDER.withProperty("facing", "south"));
}
}
}
}
}
@@ -0,0 +1,35 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class LeafHedges extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
unit.modifier().setBlock(point.withY(start.blockY()), Block.GRASS_BLOCK);
int z = relZ(point, start);
if(z % 4 != 0 && z >= 1 && z <= 14 && rnd.nextInt(10) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), BlockPallet.FLOWER.rnd());
}
});
int[] rows = {4, 8, 12};
for(int i = 0; i < rows.length; i++) {
int gapStart = i % 2 == 0 ? 11 + rnd.nextInt(3) : 1 + rnd.nextInt(3);
for(int x = 0; x < 16; x++) {
if(x >= gapStart && x < gapStart + 2) continue;
Block leaves = (rnd.nextInt(10) < 3 ? Block.FLOWERING_AZALEA_LEAVES : Block.AZALEA_LEAVES)
.withProperty("persistent", "true");
unit.modifier().setBlock(start.add(x, 1, rows[i]), leaves);
unit.modifier().setBlock(start.add(x, 2, rows[i]), leaves);
}
}
}
}
@@ -0,0 +1,40 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class MudPools extends Obstacle {
private final JNoise pools = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.11)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
int z = relZ(point, start);
double noise = this.pools.evaluateNoise(point.blockX(), point.blockZ());
boolean pool = z >= 1 && z <= 14 && noise > 0.05;
if(pool) {
unit.modifier().setBlock(point.withY(start.blockY() - 1), Block.MUD);
return;
}
Block floor = noise > -0.3 ? Block.MUDDY_MANGROVE_ROOTS : Block.GRASS_BLOCK;
unit.modifier().setBlock(point.withY(start.blockY() - 1), Block.MUD);
unit.modifier().setBlock(point.withY(start.blockY()), floor);
if(z >= 2 && z <= 13 && rnd.nextInt(6) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.MANGROVE_ROOTS);
}
});
}
}
@@ -0,0 +1,25 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
class Mudpit extends Obstacle {
private final JNoise curves = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.1)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
forEachColumn(start, point -> {
double relative = relZ(point, start);
double modifier = -1.5 * Math.cos(relative * Math.PI/7.5) + 1.5;
double heightAtBlock = start.blockY() + this.curves.evaluateNoise(point.blockX(), point.blockZ()) * modifier;
unit.modifier().setBlock(point.withY(heightAtBlock), Block.SOUL_SAND);
unit.modifier().setBlock(point.withY(heightAtBlock-1), Block.SOUL_SAND);
});
}
}
@@ -0,0 +1,37 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class MushroomRoofs extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
unit.modifier().setBlock(point.withY(start.blockY()), Block.MYCELIUM);
int z = relZ(point, start);
if(z >= 1 && z <= 14 && rnd.nextInt(20) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), rnd.nextBoolean() ? Block.RED_MUSHROOM : Block.BROWN_MUSHROOM);
}
});
for(int stemZ : new int[]{4, 8, 12}) {
int x = 4 + rnd.nextInt(8);
Block cap = rnd.nextBoolean() ? Block.RED_MUSHROOM_BLOCK : Block.BROWN_MUSHROOM_BLOCK;
unit.modifier().setBlock(start.add(x, 1, stemZ), Block.MUSHROOM_STEM);
for(int capX = x - 3; capX <= x + 3; capX++) {
for(int capZ = stemZ - 1; capZ <= stemZ + 1; capZ++) {
if(capX < 0 || capX > 15 || capZ < 0 || capZ > 15) continue;
if(Math.abs(capX - x) == 3 && Math.abs(capZ - stemZ) == 1) continue;
unit.modifier().setBlock(start.add(capX, 2, capZ), cap);
}
}
}
}
}
@@ -0,0 +1,69 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import eu.mhsl.minenet.minigames.util.GeneratorUtils;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
public abstract class Obstacle {
public static final int LENGTH = 16;
@SuppressWarnings("StaticInitializerReferencesSubClass")
private static final List<Obstacle> obstacles = List.of(
new Mudpit(),
new IceRink(),
new HoneyField(),
new CobwebGrove(),
new BerryPatch(),
new WaterChannel(),
new HayHurdles(),
new SlimePatches(),
new MudPools(),
new PowderSnowField(),
new DripstoneSpikes(),
new ChorusGrove(),
new BasaltPillars(),
new WallSlalom(),
new LeafHedges(),
new MushroomRoofs(),
new SandDunes(),
new LadderWall(),
new LadderWall2(),
new GlassPaneWeave(),
new BoulderTrench(),
new BambooThicket()
);
public abstract void generate(GenerationUnit unit, Point start);
private static final List<Obstacle> remaining = new ArrayList<>();
public static synchronized Obstacle getRandomObstacle() {
if(remaining.isEmpty()) {
remaining.addAll(obstacles);
}
int index = ThreadLocalRandom.current().nextInt(remaining.size());
return remaining.remove(index);
}
protected static void forEachColumn(Point start, Consumer<Point> callback) {
GeneratorUtils.foreachXZ(start, start.add(15, 0, LENGTH - 1), callback);
}
protected static int relX(Point point, Point start) {
return point.blockX() - start.blockX();
}
protected static int relZ(Point point, Point start) {
return point.blockZ() - start.blockZ();
}
protected static Random seededRandom(Point start) {
return new Random(start.blockX() * 341873128712L + start.blockZ() * 132897987541L);
}
}
@@ -0,0 +1,40 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class PowderSnowField extends Obstacle {
private final JNoise drifts = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.14)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
int z = relZ(point, start);
int laneX = 7 + (int) Math.round(3 * Math.sin(z * Math.PI / 7.5));
boolean onLane = relX(point, start) == laneX || relX(point, start) == laneX + 1;
double noise = this.drifts.evaluateNoise(point.blockX(), point.blockZ());
boolean drift = z >= 2 && z <= 13 && noise > (onLane ? 0.45 : 0.0);
unit.modifier().setBlock(point.withY(start.blockY()), onLane ? Block.PACKED_ICE : Block.SNOW_BLOCK);
if(drift) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.POWDER_SNOW);
if(noise > 0.6) {
unit.modifier().setBlock(point.withY(start.blockY() + 2), Block.POWDER_SNOW);
}
} else if(!onLane && rnd.nextInt(6) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.SNOW.withProperty("layers", "1"));
}
});
}
}
@@ -0,0 +1,41 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class SandDunes extends Obstacle {
private final JNoise dunes = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.08)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
double taper = -1.25 * Math.cos(relZ(point, start) * Math.PI / 7.5) + 1.25;
double noise = (this.dunes.evaluateNoise(point.blockX(), point.blockZ()) + 1) / 2;
int top = start.blockY() + (int) Math.round(noise * taper);
for(int y = start.blockY() - 1; y < top; y++) {
unit.modifier().setBlock(point.withY(y), Block.SANDSTONE);
}
unit.modifier().setBlock(point.withY(top), Block.SAND);
if(top > start.blockY() + 1 && rnd.nextInt(14) == 0) {
unit.modifier().setBlock(point.withY(top + 1), Block.DEAD_BUSH);
} else if(top == start.blockY() && relZ(point, start) >= 2 && relZ(point, start) <= 13 && rnd.nextInt(22) == 0) {
int cactusHeight = 1 + rnd.nextInt(2);
for(int y = 1; y <= cactusHeight; y++) {
unit.modifier().setBlock(point.withY(top + y), Block.CACTUS);
}
}
});
}
}
@@ -0,0 +1,31 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import de.articdive.jnoise.generators.noisegen.perlin.PerlinNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class SlimePatches extends Obstacle {
private final JNoise blobs = JNoise.newBuilder()
.perlin(PerlinNoiseGenerator.newBuilder().build())
.scale(0.18)
.build();
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
int z = relZ(point, start);
boolean slime = z >= 1 && z <= 14 && this.blobs.evaluateNoise(point.blockX(), point.blockZ()) > -0.2;
unit.modifier().setBlock(point.withY(start.blockY()), slime ? Block.SLIME_BLOCK : Block.MOSS_BLOCK);
if(!slime && z >= 2 && z <= 13 && rnd.nextInt(18) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.AZALEA);
}
});
}
}
@@ -0,0 +1,38 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class WallSlalom extends Obstacle {
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point ->
unit.modifier().setBlock(point.withY(start.blockY()), rnd.nextInt(4) == 0 ? Block.ANDESITE : Block.STONE));
int[] rows = {4, 8, 12};
for(int i = 0; i < rows.length; i++) {
int gapStart = i % 2 == 0 ? 1 + rnd.nextInt(3) : 10 + rnd.nextInt(3);
boolean[] hasWall = new boolean[16];
for(int x = 0; x < 16; x++) {
hasWall[x] = x < gapStart || x >= gapStart + 2;
}
for(int x = 0; x < 16; x++) {
if(!hasWall[x]) continue;
boolean westConnected = x > 0 && hasWall[x - 1];
boolean eastConnected = x < 15 && hasWall[x + 1];
Block wall = Block.STONE_BRICK_WALL
.withProperty("west", westConnected ? "low" : "none")
.withProperty("east", eastConnected ? "low" : "none")
.withProperty("up", String.valueOf(!westConnected || !eastConnected || x % 4 == 0));
unit.modifier().setBlock(start.add(x, 1, rows[i]), wall);
}
}
}
}
@@ -0,0 +1,37 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.playerRace.obstacles;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import java.util.Random;
class WaterChannel extends Obstacle {
private static final int[] DEPTHS = {0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, 0};
@Override
public void generate(GenerationUnit unit, Point start) {
Random rnd = seededRandom(start);
forEachColumn(start, point -> {
int depth = DEPTHS[relZ(point, start)];
if(depth == 0) {
unit.modifier().setBlock(point.withY(start.blockY()), rnd.nextInt(3) == 0 ? Block.SMOOTH_SANDSTONE : Block.SAND);
return;
}
Block bottom = rnd.nextInt(5) == 0 ? Block.DARK_PRISMARINE : Block.PRISMARINE;
unit.modifier().setBlock(point.withY(start.blockY() - depth), bottom);
for(int y = start.blockY() - depth + 1; y <= start.blockY(); y++) {
unit.modifier().setBlock(point.withY(y), Block.WATER);
}
if(depth >= 2 && rnd.nextInt(4) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() - depth + 1), Block.SEAGRASS);
}
if(rnd.nextInt(20) == 0) {
unit.modifier().setBlock(point.withY(start.blockY() + 1), Block.LILY_PAD);
}
});
}
}
@@ -125,7 +125,7 @@ public class SpaceSnake extends StatelessGame {
} }
PlayState state = this.playerStates.get(playerBlockPlaceEvent.getPlayer()); PlayState state = this.playerStates.get(playerBlockPlaceEvent.getPlayer());
state.blocks.add(playerBlockPlaceEvent.getBlockPosition().asVec().asPosition()); state.blocks.add(new Pos(playerBlockPlaceEvent.getBlockPosition()));
state.cutToLength(pos -> this.setBlock(pos, Block.AIR)); state.cutToLength(pos -> this.setBlock(pos, Block.AIR));
MinecraftServer.getSchedulerManager().scheduleNextTick(() -> this.updateInv(playerBlockPlaceEvent.getPlayer())); MinecraftServer.getSchedulerManager().scheduleNextTick(() -> this.updateInv(playerBlockPlaceEvent.getPlayer()));
@@ -28,7 +28,8 @@ public class StickFightFactory implements GameFactory {
@Override @Override
public ConfigManager configuration() { public ConfigManager configuration() {
return new ConfigManager() return new ConfigManager()
.addOption(new NumericOption("length", Material.SANDSTONE, TranslatedComponent.byId("optionCommon#length"), 7, 10, 13, 16, 19)); .addOption(new NumericOption("length", Material.SANDSTONE, TranslatedComponent.byId("optionCommon#length"), 7, 10, 13, 16, 19))
.addOption(new NumericOption("seconds", Material.CLOCK, TranslatedComponent.byId("optionCommon#seconds"), 30, 60, 90, 120));
} }
@Override @Override
@@ -40,7 +41,7 @@ public class StickFightFactory implements GameFactory {
@Override @Override
public Game manufacture(Room parent, Map<String, Option<?>> configuration) { public Game manufacture(Room parent, Map<String, Option<?>> configuration) {
return new Stickfight(configuration.get("length").getAsInt()).setParent(parent); return new Stickfight(configuration.get("length").getAsInt(), configuration.get("seconds").getAsInt()).setParent(parent);
} }
@Override @Override
@@ -24,10 +24,12 @@ public class Stickfight extends StatelessGame {
private final WeakHashMap<Player, Pos> spawnPoints = new WeakHashMap<>(); private final WeakHashMap<Player, Pos> spawnPoints = new WeakHashMap<>();
private final Map<Player, Integer> scoreMap = new WeakHashMap<>(); private final Map<Player, Integer> scoreMap = new WeakHashMap<>();
private boolean countdownStarted = false; private boolean countdownStarted = false;
private final int seconds;
public Stickfight(int length) { public Stickfight(int length, int seconds) {
super(Dimension.OVERWORLD.key, "Stickfight", new LowestPointsWinScore()); super(Dimension.OVERWORLD.key, "Stickfight", new LowestPointsWinScore());
this.radius = length; this.radius = length;
this.seconds = seconds;
this.eventNode().addChild( this.eventNode().addChild(
CombatFeatures.empty() CombatFeatures.empty()
@@ -88,6 +90,11 @@ public class Stickfight extends StatelessGame {
super.start(); super.start();
} }
@Override
protected void onStart() {
this.setTimeLimit(this.seconds);
}
@Override @Override
protected void onStop() { protected void onStop() {
this.scoreMap.forEach((player, score) -> this.getScore().insertResult(player, score)); this.scoreMap.forEach((player, score) -> this.getScore().insertResult(player, score));
@@ -32,7 +32,7 @@ public class Sumo extends StatelessGame {
super(Dimension.OVERWORLD.key, "Sumo", new LastWinsScore()); super(Dimension.OVERWORLD.key, "Sumo", new LastWinsScore());
this.getScore().setIgnoreLastPlayers(1); this.getScore().setIgnoreLastPlayers(1);
this.setTime(6000); this.setTime(6000);
this.setTimeRate(0); this.defaultClock().pause();
this.radius = radius; this.radius = radius;
this.health = health; this.health = health;
@@ -6,18 +6,20 @@ import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game.Tetri
import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game.Tetromino; import eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game.Tetromino;
import eu.mhsl.minenet.minigames.score.PointsWinScore; import eu.mhsl.minenet.minigames.score.PointsWinScore;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Entity; import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.GameMode; import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.entity.metadata.display.BlockDisplayMeta;
import net.minestom.server.event.player.PlayerHandAnimationEvent; import net.minestom.server.event.player.PlayerHandAnimationEvent;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.event.player.PlayerTickEvent; import net.minestom.server.event.player.PlayerTickEvent;
import net.minestom.server.event.player.PlayerUseItemEvent; import net.minestom.server.event.player.PlayerUseItemEvent;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import net.minestom.server.potion.Potion; import net.minestom.server.timer.TaskSchedule;
import net.minestom.server.potion.PotionEffect;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Map; import java.util.Map;
@@ -78,18 +80,24 @@ class Tetris extends StatelessGame {
p.setGameMode(GameMode.SURVIVAL); p.setGameMode(GameMode.SURVIVAL);
} }
@Override private void onPlayerInteract(@NotNull PlayerUseItemEvent event) {
protected void onPlayerMove(@NotNull PlayerMoveEvent event) { event.setItemUseTime(0);
this.tetrisGames.get(event.getPlayer()).pressedButton(TetrisGame.Button.mouseRight);
}
private void onPlayerAttack(@NotNull PlayerHandAnimationEvent event) {
this.tetrisGames.get(event.getPlayer()).pressedButton(TetrisGame.Button.mouseLeft);
}
private void onPlayerTick(PlayerTickEvent event) {
Player player = event.getPlayer(); Player player = event.getPlayer();
Pos currentPosition = event.getNewPosition();
TetrisGame tetrisGame = this.tetrisGames.get(player); TetrisGame tetrisGame = this.tetrisGames.get(player);
if(tetrisGame == null) return;
if(tetrisGame == null) { if(tetrisGame.lost) {
event.setCancelled(true); this.letPlayerLoose(player);
return; return;
} }
if(tetrisGame.lost) return;
if(player.getGameMode() == GameMode.SPECTATOR) return; if(player.getGameMode() == GameMode.SPECTATOR) return;
if(player.inputs().forward()) tetrisGame.pressedButton(TetrisGame.Button.W); if(player.inputs().forward()) tetrisGame.pressedButton(TetrisGame.Button.W);
@@ -97,26 +105,6 @@ class Tetris extends StatelessGame {
if(player.inputs().right()) tetrisGame.pressedButton(TetrisGame.Button.D); if(player.inputs().right()) tetrisGame.pressedButton(TetrisGame.Button.D);
if(player.inputs().left()) tetrisGame.pressedButton(TetrisGame.Button.A); if(player.inputs().left()) tetrisGame.pressedButton(TetrisGame.Button.A);
if(player.inputs().jump()) tetrisGame.pressedButton(TetrisGame.Button.space); 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) { private void letPlayerLoose(Player player) {
@@ -124,6 +112,7 @@ class Tetris extends StatelessGame {
if(!this.getScore().hasResult(player)) { if(!this.getScore().hasResult(player)) {
player.setGameMode(GameMode.SPECTATOR); player.setGameMode(GameMode.SPECTATOR);
player.setInvisible(true); player.setInvisible(true);
if(player.getVehicle() != null) player.getVehicle().removePassenger(player);
this.getScore().insertResult(player, tetrisGame.getScore()); this.getScore().insertResult(player, tetrisGame.getScore());
} }
@@ -160,7 +149,16 @@ class Tetris extends StatelessGame {
p.teleport(tetrisGame.getPlayerSpawnPosition()); p.teleport(tetrisGame.getPlayerSpawnPosition());
tetrisGame.sidebar.addViewer(p); tetrisGame.sidebar.addViewer(p);
p.addEffect(new Potion(PotionEffect.SLOWNESS, 4, Potion.INFINITE_DURATION));
MinecraftServer.getSchedulerManager().scheduleTask(() -> {
Entity ghostBlock = new Entity(EntityType.BLOCK_DISPLAY);
((BlockDisplayMeta) ghostBlock.getEntityMeta()).setBlockState(Block.AIR);
ghostBlock.setNoGravity(true);
ghostBlock.setInstance(this, tetrisGame.getPlayerSpawnPosition());
ghostBlock.addPassenger(p);
return TaskSchedule.stop();
}, TaskSchedule.nextTick());
return super.onPlayerJoin(p); return super.onPlayerJoin(p);
} }
@@ -0,0 +1,17 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;
public enum Orientation {
NONE,
RIGHT,
LEFT,
UPSIDE_DOWN;
public Orientation rotated(boolean clockwise) {
return switch(this) {
case NONE -> clockwise ? Orientation.RIGHT : Orientation.LEFT;
case RIGHT -> clockwise ? Orientation.UPSIDE_DOWN : Orientation.NONE;
case UPSIDE_DOWN -> clockwise ? Orientation.LEFT : Orientation.RIGHT;
case LEFT -> clockwise ? Orientation.NONE : Orientation.UPSIDE_DOWN;
};
}
}
@@ -0,0 +1,53 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.tetris.game;
import java.util.Map;
import java.util.stream.IntStream;
public final class RotationChecker {
private static final Map<Orientation, int[][]> STANDARD_WALL_KICKS = Map.of(
Orientation.NONE, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
Orientation.RIGHT, new int[][] {{0,0}, {1,0}, {1,-1}, {0,2}, {1,2}},
Orientation.UPSIDE_DOWN, new int[][] {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}},
Orientation.LEFT, new int[][] {{0,0}, {-1,0}, {-1,-1}, {0,2}, {-1,2}}
);
private static final Map<Orientation, int[][]> I_WALL_KICKS = Map.of(
Orientation.NONE, new int[][] {{0,0}, {-1,0}, {2,0}, {-1,0}, {2,0}},
Orientation.RIGHT, new int[][] {{-1,0}, {0,0}, {0,0}, {0,1}, {0,-2}},
Orientation.UPSIDE_DOWN, new int[][] {{-1,1}, {1,1}, {-2,1}, {1,0}, {-2,0}},
Orientation.LEFT, new int[][] {{0,1}, {0,1}, {0,1}, {0,-1}, {0,2}}
);
private static final Map<Orientation, int[][]> O_WALL_KICKS = Map.of(
Orientation.NONE, new int[][] {{0,0}},
Orientation.RIGHT, new int[][] {{0,-1}},
Orientation.UPSIDE_DOWN, new int[][] {{-1,-1}},
Orientation.LEFT, new int[][] {{-1,0}}
);
public static int[][] getKicksArray(Orientation from, Orientation to, Tetromino.Shape shape) {
int[][] firstOffsets = getOffsetData(from, shape);
int[][] secondOffsets = getOffsetData(to, shape);
int[][] result = new int[firstOffsets.length][2];
for(int i = 0; i < firstOffsets.length; i++) {
result[i] = subtractInt(firstOffsets[i], secondOffsets[i]);
}
return result;
}
private static int[] subtractInt(int[] first, int[] second) {
return IntStream.range(0, first.length)
.map(i -> first[i] - second[i])
.toArray();
}
private static int[][] getOffsetData(Orientation orientation, Tetromino.Shape shape) {
return switch(shape) {
case J, L, S, T, Z -> STANDARD_WALL_KICKS.get(orientation);
case I -> I_WALL_KICKS.get(orientation);
case O -> O_WALL_KICKS.get(orientation);
};
}
}
@@ -2,10 +2,9 @@ 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.instance.game.stateless.StatelessGame;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos; import net.minestom.server.coordinate.Pos;
import net.minestom.server.scoreboard.Sidebar; import net.minestom.server.scoreboard.Sidebar;
import net.minestom.server.timer.Scheduler; import net.minestom.server.timer.Task;
import net.minestom.server.timer.TaskSchedule; import net.minestom.server.timer.TaskSchedule;
import java.util.*; import java.util.*;
@@ -23,6 +22,9 @@ public class TetrisGame {
private final Map<Button, Long> lastPresses = new HashMap<>(); private final Map<Button, Long> lastPresses = new HashMap<>();
private final List<TetrisGame> otherTetrisGames = new ArrayList<>(); private final List<TetrisGame> otherTetrisGames = new ArrayList<>();
private final Random random; private final Random random;
private Task tetrominoLockTask;
private int lockDelayResets;
private int currentTetrominoLowestY = Integer.MAX_VALUE;
public boolean lost = false; public boolean lost = false;
public boolean paused = true; public boolean paused = true;
public Tetromino currentTetromino; public Tetromino currentTetromino;
@@ -54,24 +56,27 @@ public class TetrisGame {
} }
public void pressedButton(Button button) { public void pressedButton(Button button) {
final int standardButtonDelay = 100; final int standardButtonDelay = 95;
final int buttonDebounce = 70; final int wsButtonDebounce = 70;
if(this.lastPresses.getOrDefault(button, 0L) >= System.currentTimeMillis() - standardButtonDelay) return; if(this.lastPresses.getOrDefault(button, 0L) >= System.currentTimeMillis() - standardButtonDelay) return;
this.lastPresses.put(button, System.currentTimeMillis()); switch(button) {
if(button == Button.W) this.lastPresses.put(button, System.currentTimeMillis() + buttonDebounce); case W -> this.lastPresses.put(button, System.currentTimeMillis() + wsButtonDebounce);
if(button == Button.S) this.lastPresses.put(button, System.currentTimeMillis() - buttonDebounce); case S -> this.lastPresses.put(button, System.currentTimeMillis() - wsButtonDebounce);
case mouseLeft, mouseRight -> this.lastPresses.put(button, 0L);
default -> this.lastPresses.put(button, System.currentTimeMillis());
}
if(this.lost || this.paused) return; if(this.lost || this.paused) return;
switch(button) { switch(button) {
case A -> this.currentTetromino.moveLeft(); case A -> this.moveLeft();
case S -> this.moveDown(); case S -> this.moveDown();
case D -> this.currentTetromino.moveRight(); case D -> this.moveRight();
case W -> this.hardDrop(); case W -> this.hardDrop();
case mouseLeft -> this.currentTetromino.rotate(false); case mouseLeft -> this.rotate(false);
case mouseRight -> this.currentTetromino.rotate(true); case mouseRight -> this.rotate(true);
case space -> this.switchHold(); case space -> this.switchHold();
} }
} }
@@ -82,8 +87,7 @@ public class TetrisGame {
public void start() { public void start() {
this.paused = false; this.paused = false;
Scheduler scheduler = MinecraftServer.getSchedulerManager(); this.instance.scheduler().submitTask(() -> {
scheduler.submitTask(() -> {
if(this.lost) return TaskSchedule.stop(); if(this.lost) return TaskSchedule.stop();
int standardTickDelay = 40; int standardTickDelay = 40;
if(this.isFast) standardTickDelay = 20; if(this.isFast) standardTickDelay = 20;
@@ -111,9 +115,12 @@ public class TetrisGame {
public void tick() { public void tick() {
if(this.lost || this.paused) return; if(this.lost || this.paused) return;
if(!this.currentTetromino.moveDown()) { if(this.currentTetromino.moveDown()) {
this.setActiveTetrominoDown(); this.stopTetrominoLockTask(this.currentTetromino.getYPosition() < this.currentTetrominoLowestY, false);
} else {
this.scheduleTetrominoLock();
} }
this.currentTetrominoLowestY = Math.min(this.currentTetrominoLowestY, this.currentTetromino.getYPosition());
} }
public int getScore() { public int getScore() {
@@ -139,33 +146,43 @@ public class TetrisGame {
this.lost = true; this.lost = true;
} }
private boolean moveDown() { private void moveDown() {
if(!this.currentTetromino.moveDown()) { if(!this.currentTetromino.moveDown()) {
this.setActiveTetrominoDown(); this.scheduleTetrominoLock();
return false; return;
} }
this.stopTetrominoLockTask(this.currentTetromino.getYPosition() < this.currentTetrominoLowestY, false);
this.score += 1; this.score += 1;
this.updateInfo(); this.updateInfo();
return true;
} }
private boolean hardDrop() { private void moveLeft() {
if(this.currentTetromino.moveLeft()) this.onSuccessfulMoveOrRotate();
}
private void moveRight() {
if(this.currentTetromino.moveRight()) this.onSuccessfulMoveOrRotate();
}
private void rotate(boolean clockwise) {
if(this.currentTetromino.rotate(clockwise)) this.onSuccessfulMoveOrRotate();
}
private void hardDrop() {
if(!this.currentTetromino.moveDown()) { if(!this.currentTetromino.moveDown()) {
this.setActiveTetrominoDown(); this.lockActiveTetromino();
return false; return;
} }
do {
this.score += 2; this.score += 2;
} while(this.currentTetromino.moveDown());
this.updateInfo(); this.updateInfo();
while(this.currentTetromino.moveDown()) { this.lockActiveTetromino();
this.score += 2;
this.updateInfo();
}
this.setActiveTetrominoDown();
return true;
} }
private boolean switchHold() { private void switchHold() {
if(!this.holdPossible) return false; if(!this.holdPossible) return;
this.stopTetrominoLockTask(true);
Tetromino newCurrentTetromino; Tetromino newCurrentTetromino;
if(this.holdTetromino == null) { if(this.holdTetromino == null) {
@@ -181,6 +198,7 @@ public class TetrisGame {
this.holdTetromino = new Tetromino(this.instance, this.currentTetromino.getShape()); this.holdTetromino = new Tetromino(this.instance, this.currentTetromino.getShape());
this.currentTetromino = newCurrentTetromino; this.currentTetromino = newCurrentTetromino;
this.currentTetrominoLowestY = Integer.MAX_VALUE;
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw(); this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) this.loose(); if(!this.currentTetromino.moveDown()) this.loose();
@@ -189,7 +207,12 @@ public class TetrisGame {
this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0)); this.holdTetromino.setPosition(this.holdPosition.add(xChange, 0, 0));
this.holdTetromino.drawAsEntities(); this.holdTetromino.drawAsEntities();
this.holdPossible = false; this.holdPossible = false;
return true; }
private void onSuccessfulMoveOrRotate() {
if(!this.currentTetromino.isGrounded()) return;
this.stopTetrominoLockTask(false);
this.scheduleTetrominoLock();
} }
private void updateNextTetrominoes() { private void updateNextTetrominoes() {
@@ -235,11 +258,39 @@ public class TetrisGame {
this.sidebar.updateLineScore("2", this.level); this.sidebar.updateLineScore("2", this.level);
} }
private void setActiveTetrominoDown() { private void scheduleTetrominoLock() {
if(this.tetrominoLockTask == null || !this.tetrominoLockTask.isAlive())
this.tetrominoLockTask = this.instance.scheduler().scheduleTask(() -> {
if(this.currentTetromino.isGrounded()) {
this.lockActiveTetromino();
} else {
this.stopTetrominoLockTask(false, false);
}
return TaskSchedule.stop();
}, TaskSchedule.millis(500));
}
private void stopTetrominoLockTask(boolean resetHard) {
this.stopTetrominoLockTask(resetHard, true);
}
private void stopTetrominoLockTask(boolean resetHard, boolean addToResets) {
if(resetHard) this.lockDelayResets = 0;
if(this.lockDelayResets >= 15 && addToResets) {
this.lockActiveTetromino();
return;
}
if(this.tetrominoLockTask != null) {
this.tetrominoLockTask.cancel();
this.tetrominoLockTask = null;
if(!resetHard && addToResets) this.lockDelayResets++;
}
}
private void lockActiveTetromino() {
this.stopTetrominoLockTask(true);
this.currentTetromino.removeOwnEntities(); this.currentTetromino.removeOwnEntities();
this.currentTetromino = this.nextTetrominoes.removeFirst();
this.currentTetromino.remove();
this.updateNextTetrominoes();
int removedLines = this.playfield.removeFullLines(); int removedLines = this.playfield.removeFullLines();
int combatLines = 0; int combatLines = 0;
@@ -297,6 +348,11 @@ public class TetrisGame {
this.updateInfo(); this.updateInfo();
this.currentTetromino = this.nextTetrominoes.removeFirst();
this.currentTetromino.remove();
this.updateNextTetrominoes();
this.currentTetrominoLowestY = Integer.MAX_VALUE;
this.currentTetromino.setPosition(this.tetrominoSpawnPosition); this.currentTetromino.setPosition(this.tetrominoSpawnPosition);
this.currentTetromino.draw(); this.currentTetromino.draw();
if(!this.currentTetromino.moveDown()) { if(!this.currentTetromino.moveDown()) {
@@ -8,14 +8,12 @@ import net.minestom.server.entity.metadata.other.FallingBlockMeta;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.tag.Tag; import net.minestom.server.tag.Tag;
import java.util.ArrayList; import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
public class Tetromino { public class Tetromino {
private final static EntityType ghostEntityType = EntityType.FALLING_BLOCK; private final static EntityType ghostEntityType = EntityType.FALLING_BLOCK;
private final static Tag<String> uuidTag = Tag.String("uuid"); private final static Tag<String> uuidTag = Tag.String("uuid");
private Orientation orientation = Orientation.NONE;
private final Shape shape; private final Shape shape;
private final StatelessGame instance; private final StatelessGame instance;
private final UUID uuid; private final UUID uuid;
@@ -28,10 +26,10 @@ public class Tetromino {
this.uuid = UUID.randomUUID(); this.uuid = UUID.randomUUID();
switch(this.shape) { 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 I -> this.shapeArray = new int[][]{{0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 1, 1, 1, 1}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}};
case J -> this.shapeArray = new int[][]{{1, 0, 0}, {1, 1, 1}, {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 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 O -> this.shapeArray = new int[][]{{0, 1, 1}, {0, 1, 1}, {0, 0, 0}};
case S -> this.shapeArray = new int[][]{{0, 1, 1}, {1, 1, 0}, {0, 0, 0}}; 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 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}}; case Z -> this.shapeArray = new int[][]{{1, 1, 0}, {0, 1, 1}, {0, 0, 0}};
@@ -43,12 +41,23 @@ public class Tetromino {
} }
public void setPosition(Pos newPosition) { public void setPosition(Pos newPosition) {
this.position = newPosition; this.position = new Pos(newPosition.x(), newPosition.y(), newPosition.z());
} }
public boolean rotate(boolean clockwise) { public boolean rotate(boolean clockwise) {
int[][] newShapeArray = this.getTurnedShapeArray(clockwise); int[][] newShapeArray = this.getTurnedShapeArray(clockwise);
return this.checkCollisionAndMove(this.position, newShapeArray); Orientation newOrientation = this.orientation.rotated(clockwise);
int[][] kicksArray = RotationChecker.getKicksArray(this.orientation, newOrientation, this.shape);
for(int[] k : kicksArray) {
Pos candidate = new Pos(this.position.x() + k[0], this.position.y() + k[1], this.position.z());
if(this.checkCollisionAndMove(candidate, newShapeArray)) {
this.orientation = newOrientation;
return true;
}
}
return false;
} }
public boolean moveDown() { public boolean moveDown() {
@@ -73,7 +82,7 @@ public class Tetromino {
public void draw(boolean withGhost) { public void draw(boolean withGhost) {
if(withGhost) { if(withGhost) {
Pos ghostPos = this.position; Pos ghostPos = this.position;
while(!this.checkCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) { while(!this.hasCollision(ghostPos.sub(0, 1, 0), this.shapeArray)) {
ghostPos = ghostPos.sub(0, 1, 0); ghostPos = ghostPos.sub(0, 1, 0);
} }
Pos positionChange = this.position.sub(ghostPos); Pos positionChange = this.position.sub(ghostPos);
@@ -182,7 +191,7 @@ public class Tetromino {
private boolean isPartOfTetromino(Pos position) { private boolean isPartOfTetromino(Pos position) {
return this.getBlockPositions().stream() return this.getBlockPositions().stream()
.anyMatch(pos -> pos.equals(position)); .anyMatch(pos -> pos.sameBlock(position));
} }
private List<Pos> getBlockPositions() { private List<Pos> getBlockPositions() {
@@ -198,10 +207,10 @@ public class Tetromino {
for(int x = 0; x < arrayLength; x++) { for(int x = 0; x < arrayLength; x++) {
for(int y = 0; y < arrayLength; y++) { for(int y = 0; y < arrayLength; y++) {
if(shapeArray[arrayLength - 1 - y][x] == 1) { if(shapeArray[arrayLength - 1 - y][x] == 1) {
switch(this.shape) { if(Objects.requireNonNull(this.shape) == Shape.I) {
case I -> returnList.add(position.add(x - 1, y - 2, 0)); returnList.add(position.add(x - 2, y - 2, 0));
case O -> returnList.add(position.add(x, y, 0)); } else {
default -> returnList.add(position.add(x - 1, y - 1, 0)); returnList.add(position.add(x - 1, y - 1, 0));
} }
} }
} }
@@ -210,7 +219,7 @@ public class Tetromino {
return returnList; return returnList;
} }
private boolean checkCollision(Pos newPosition, int[][] newShapeArray) { private boolean hasCollision(Pos newPosition, int[][] newShapeArray) {
List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray); List<Pos> newBlockPositions = this.getBlockPositions(newPosition, newShapeArray);
for(Pos pos : newBlockPositions) { for(Pos pos : newBlockPositions) {
@@ -222,15 +231,21 @@ public class Tetromino {
return false; return false;
} }
public boolean isGrounded() {
return this.hasCollision(this.position.sub(0, 1, 0), this.shapeArray);
}
private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) { private boolean checkCollisionAndMove(Pos newPosition, int[][] newShapeArray) {
if(!this.checkCollision(newPosition, newShapeArray)) { if(this.hasCollision(newPosition, newShapeArray)) return false;
this.remove(); this.remove();
this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new); this.shapeArray = Arrays.stream(newShapeArray).map(int[]::clone).toArray(int[][]::new);
this.setPosition(newPosition); this.setPosition(newPosition);
this.draw(); this.draw();
return true; return true;
} }
return false;
public int getYPosition() {
return Math.toIntExact(Math.round(this.position.y()));
} }
public enum Shape { public enum Shape {
@@ -22,7 +22,12 @@ public class TowerdefenseRoom extends InstanceContainer {
private final Towerdefense game; private final Towerdefense game;
public TowerdefenseRoom(Player player, Towerdefense game) { public TowerdefenseRoom(Player player, Towerdefense game) {
super(UUID.randomUUID(), Dimension.OVERWORLD.key); this(UUID.randomUUID(), player, game);
}
// eindeutiger Dimension-Name pro Instanz, siehe MineNetInstance
private TowerdefenseRoom(UUID uuid, Player player, Towerdefense game) {
super(uuid, Dimension.OVERWORLD.key, net.kyori.adventure.key.Key.key("minenet", "instance-" + uuid));
MinecraftServer.getInstanceManager().registerInstance(this); MinecraftServer.getInstanceManager().registerInstance(this);
this.player = player; this.player = player;
this.game = game; this.game = game;
@@ -1,6 +1,7 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.trafficlightrace; package eu.mhsl.minenet.minigames.instance.game.stateless.types.trafficlightrace;
import de.articdive.jnoise.JNoise; import de.articdive.jnoise.generators.noisegen.opensimplex.FastSimplexNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import eu.mhsl.minenet.minigames.instance.Dimension; import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame; import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.FirstWinsScore; import eu.mhsl.minenet.minigames.score.FirstWinsScore;
@@ -49,9 +50,8 @@ class TrafficLightRace extends StatelessGame {
@Override @Override
protected void onLoad(@NotNull CompletableFuture<Void> callback) { protected void onLoad(@NotNull CompletableFuture<Void> callback) {
final JNoise slowBatches = JNoise.newBuilder() final JNoise slowBatches = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.1)
.setFrequency(0.1)
.build(); .build();
AbsoluteBlockBatch batch = new AbsoluteBlockBatch(); AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
@@ -62,7 +62,7 @@ class TrafficLightRace extends StatelessGame {
} }
for(int z = this.preRun; z <= this.length + this.preRun; z++) { for(int z = this.preRun; z <= this.length + this.preRun; z++) {
batch.setBlock(x, 50, z, slowBatches.getNoise(x, z) > 0.6 ? Block.SOUL_SAND : BlockPallet.STREET.rnd()); batch.setBlock(x, 50, z, slowBatches.evaluateNoise(x, z) > 0.6 ? Block.SOUL_SAND : BlockPallet.STREET.rnd());
} }
for(int z = this.preRun + this.length; z <= this.preRun + this.length + this.afterRun; z++) { for(int z = this.preRun + this.length; z <= this.preRun + this.length + this.afterRun; z++) {
@@ -15,8 +15,7 @@ import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.GameMode; import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.entity.metadata.other.FallingBlockMeta; import net.minestom.server.entity.metadata.other.FallingBlockMeta;
import net.minestom.server.event.player.PlayerStartSneakingEvent; import net.minestom.server.event.player.PlayerInputEvent;
import net.minestom.server.event.player.PlayerStopSneakingEvent;
import net.minestom.server.event.player.PlayerTickEvent; import net.minestom.server.event.player.PlayerTickEvent;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
@@ -45,8 +44,7 @@ class TurtleGame extends StatelessGame {
this.eventNode() this.eventNode()
.addListener(PlayerTickEvent.class, this::onPlayerTick) .addListener(PlayerTickEvent.class, this::onPlayerTick)
.addListener(PlayerStartSneakingEvent.class, this::onPlayerStartSneak) .addListener(PlayerInputEvent.class, this::onPlayerInput);
.addListener(PlayerStopSneakingEvent.class, this::onPlayerStopSneak);
} }
@Override @Override
@@ -66,14 +64,10 @@ class TurtleGame extends StatelessGame {
} }
} }
private void onPlayerStartSneak(@NotNull PlayerStartSneakingEvent event) { private void onPlayerInput(@NotNull PlayerInputEvent event) {
Player p = event.getPlayer(); Player p = event.getPlayer();
this.turtlePlayerMap.get(p).boostSpeed(); if(event.hasPressedShiftKey()) this.turtlePlayerMap.get(p).boostSpeed();
} if(event.hasReleasedShiftKey()) this.turtlePlayerMap.get(p).cancelBoost();
private void onPlayerStopSneak(@NotNull PlayerStopSneakingEvent event) {
Player p = event.getPlayer();
this.turtlePlayerMap.get(p).cancelBoost();
} }
protected void onPlayerTick(@NotNull PlayerTickEvent event) { protected void onPlayerTick(@NotNull PlayerTickEvent event) {
@@ -1,6 +1,7 @@
package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.gameObjects; package eu.mhsl.minenet.minigames.instance.game.stateless.types.turtleGame.gameObjects;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec; import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.Entity; import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityCreature; import net.minestom.server.entity.EntityCreature;
@@ -52,7 +53,7 @@ public class Turtle extends EntityCreature {
if(this.getInstance() == null) return; if(this.getInstance() == null) return;
this.teleport(this.getPosition().withView(this.player.getPosition().withPitch(this.getPosition().pitch()))); this.teleport(this.getPosition().withView(this.player.getPosition().withPitch(this.getPosition().pitch())));
Vec lookingVector = this.player.getPosition().direction().withY(0).mul(100); Vec lookingVector = this.player.getPosition().direction().withY(0).mul(100);
this.lookAt(this.getPosition().add(lookingVector.asPosition())); this.lookAt(this.getPosition().add(new Pos(lookingVector)));
} }
public void move() { public void move() {
@@ -36,7 +36,7 @@ public class Hub extends MineNetInstance implements Spawnable {
this.setChunkLoader(new AnvilLoader(Path.of("maps/hub"))); this.setChunkLoader(new AnvilLoader(Path.of("maps/hub")));
this.setTime(18000); this.setTime(18000);
this.setTimeRate(0); this.defaultClock().pause();
} }
@@ -9,10 +9,10 @@ import eu.mhsl.minenet.minigames.shared.inventory.InteractableInventory;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.inventory.InventoryPreClickEvent;
import net.minestom.server.event.player.PlayerPacketEvent; import net.minestom.server.event.player.PlayerPacketEvent;
import net.minestom.server.inventory.InventoryType; import net.minestom.server.inventory.InventoryType;
import net.minestom.server.inventory.click.ClickType; import net.minestom.server.inventory.click.Click;
import net.minestom.server.inventory.condition.InventoryConditionResult;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import net.minestom.server.network.packet.client.play.ClientNameItemPacket; import net.minestom.server.network.packet.client.play.ClientNameItemPacket;
@@ -45,9 +45,9 @@ public class JoinInventory extends InteractableInventory {
} }
@Override @Override
protected void onClick(Player player, int slot, ClickType clickType, InventoryConditionResult inventoryConditionResult) { protected void onClick(Player player, int slot, Click click, InventoryPreClickEvent event) {
if(slot != 2) return; if(slot != 2) return;
inventoryConditionResult.setCancel(true); event.setCancelled(true);
player.closeInventory(); player.closeInventory();
this.typedText = this.formatInput(this.typedText); this.typedText = this.formatInput(this.typedText);
@@ -2,13 +2,8 @@ package eu.mhsl.minenet.minigames.message.type;
import eu.mhsl.minenet.minigames.message.TranslatableMessage; import eu.mhsl.minenet.minigames.message.TranslatableMessage;
import net.kyori.adventure.audience.Audience; import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title; import net.kyori.adventure.title.Title;
import net.kyori.adventure.title.TitlePart;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.time.Duration; import java.time.Duration;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -41,26 +36,6 @@ public class TitleMessage extends TranslatableMessage {
@Override @Override
public void send(Player p) { public void send(Player p) {
Audience.audience(p).showTitle(new Title() { Audience.audience(p).showTitle(Title.title(this.build(p), this.subtitle.build(p), this.times));
@Override
public @NotNull Component title() {
return TitleMessage.this.build(p);
}
@Override
public @NotNull Component subtitle() {
return TitleMessage.this.subtitle.build(p);
}
@Override
public @Nullable Times times() {
return TitleMessage.this.times;
}
@Override
public <T> @UnknownNullability T part(@NotNull TitlePart<T> part) {
return null;
}
});
} }
} }
@@ -30,6 +30,11 @@ public abstract class Score {
throw new NotImplementedException("This Score type is not able to process points"); throw new NotImplementedException("This Score type is not able to process points");
} }
public void insertMultiple(Set<Player> p) {
p.forEach(player -> this.insertResultProcessor(player, () -> {}));
this.insertResultImplementation(p);
}
public void insertResult(Player p) { public void insertResult(Player p) {
this.insertResultProcessor(p, () -> this.insertResultImplementation(Set.of(p))); this.insertResultProcessor(p, () -> this.insertResultImplementation(Set.of(p)));
} }
@@ -44,11 +44,11 @@ public class Tournament {
} }
return new Rewards( return new Rewards(
this.memorialConfiguration.memorialMaterial().namespace().value(), this.memorialConfiguration.memorialMaterial().key().value(),
this.memorialConfiguration.memorialTitle(), this.memorialConfiguration.memorialTitle(),
this.memorialConfiguration.memorialLore(), this.memorialConfiguration.memorialLore(),
this.getGameScores().keySet().stream().map(Player::getUuid).toList(), this.getGameScores().keySet().stream().map(Player::getUuid).toList(),
this.rewardConfiguration.item().namespace().value(), this.rewardConfiguration.item().key().value(),
itemCount itemCount
); );
} }
@@ -2,24 +2,40 @@ package eu.mhsl.minenet.minigames.shared.inventory;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.event.inventory.InventoryPreClickEvent;
import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.InventoryType; import net.minestom.server.inventory.InventoryType;
import net.minestom.server.inventory.click.ClickType; import net.minestom.server.inventory.click.Click;
import net.minestom.server.inventory.condition.InventoryConditionResult;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer; import java.util.function.Consumer;
public class InteractableInventory extends Inventory { public class InteractableInventory extends Inventory {
private final Map<Integer, ClickAction> clickActions = new ConcurrentHashMap<>();
private record ClickAction(ItemStack item, Consumer<ItemClick> callback, boolean closeAfter) {
}
/** /**
* Defines an Inventory with direct callbacks for ItemSlots * Defines an Inventory with direct callbacks for ItemSlots
*/ */
protected InteractableInventory(@NotNull InventoryType inventoryType, @NotNull Component title) { protected InteractableInventory(@NotNull InventoryType inventoryType, @NotNull Component title) {
super(inventoryType, title); super(inventoryType, title);
this.addInventoryCondition(this::onClick); this.eventNode().addListener(InventoryPreClickEvent.class, event -> {
event.setCancelled(true);
this.onClick(event.getPlayer(), event.getSlot(), event.getClick(), event);
ClickAction action = this.clickActions.get(event.getSlot());
if(action == null) return;
if(action.closeAfter()) event.getPlayer().closeInventory();
action.callback().accept(new ItemClick(event.getPlayer(), this, event.getSlot(), action.item(), event.getClick()));
});
} }
/** /**
@@ -27,13 +43,7 @@ public class InteractableInventory extends Inventory {
*/ */
protected void setClickableItem(ItemStack item, int slot, Consumer<ItemClick> callback, boolean closeAfter) { protected void setClickableItem(ItemStack item, int slot, Consumer<ItemClick> callback, boolean closeAfter) {
this.setItemStack(slot, item); this.setItemStack(slot, item);
this.addInventoryCondition((player, clickedSlot, clickType, inventoryConditionResult) -> { this.clickActions.put(slot, new ClickAction(item, callback, closeAfter));
if(clickedSlot == slot) {
if(closeAfter) player.closeInventory();
callback.accept(new ItemClick(player, this, clickedSlot, item, clickType));
}
inventoryConditionResult.setCancel(true);
});
} }
protected void setClickableItem(ItemStack item, int slot, Consumer<ItemClick> callback) { protected void setClickableItem(ItemStack item, int slot, Consumer<ItemClick> callback) {
@@ -62,7 +72,7 @@ public class InteractableInventory extends Inventory {
/** /**
* You may want to Override this method to get more generic click events * You may want to Override this method to get more generic click events
*/ */
protected void onClick(Player player, int slot, ClickType clickType, InventoryConditionResult inventoryConditionResult) { protected void onClick(Player player, int slot, Click click, InventoryPreClickEvent event) {
} }
} }
@@ -2,7 +2,7 @@ package eu.mhsl.minenet.minigames.shared.inventory;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.inventory.Inventory; import net.minestom.server.inventory.Inventory;
import net.minestom.server.inventory.click.ClickType; import net.minestom.server.inventory.click.Click;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
public class ItemClick { public class ItemClick {
@@ -10,17 +10,17 @@ public class ItemClick {
private final InteractableInventory inventory; private final InteractableInventory inventory;
private final int clickedSlot; private final int clickedSlot;
private final ItemStack item; private final ItemStack item;
private final ClickType clickType; private final Click click;
/** /**
* Describes a click on an Item from an IntractableInventory * Describes a click on an Item from an IntractableInventory
*/ */
public ItemClick(Player player, InteractableInventory inventory, int clickedSlot, ItemStack item, ClickType clickType) { public ItemClick(Player player, InteractableInventory inventory, int clickedSlot, ItemStack item, Click click) {
this.player = player; this.player = player;
this.inventory = inventory; this.inventory = inventory;
this.clickedSlot = clickedSlot; this.clickedSlot = clickedSlot;
this.item = item; this.item = item;
this.clickType = clickType; this.click = click;
} }
public Player getPlayer() { public Player getPlayer() {
@@ -39,7 +39,7 @@ public class ItemClick {
return this.item; return this.item;
} }
public ClickType getClickType() { public Click getClick() {
return this.clickType; return this.click;
} }
} }
@@ -2,6 +2,7 @@ package eu.mhsl.minenet.minigames.util;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import net.minestom.server.instance.InstanceContainer; import net.minestom.server.instance.InstanceContainer;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.batch.Batch; import net.minestom.server.instance.batch.Batch;
import net.minestom.server.instance.batch.ChunkBatch; import net.minestom.server.instance.batch.ChunkBatch;
import net.minestom.server.utils.chunk.ChunkUtils; import net.minestom.server.utils.chunk.ChunkUtils;
@@ -24,27 +25,19 @@ public class BatchUtil {
} }
} }
public static void loadAndApplyBatch(Batch batch, InstanceContainer instance, Runnable onFinish) { public static void loadAndApplyBatch(AbsoluteBlockBatch batch, InstanceContainer instance, Runnable onFinish) {
batch.awaitReady(); batch.awaitReady();
long[] affectedChunks = BatchUtil.getAffectedChunks(batch); long[] affectedChunks = BatchUtil.getAffectedChunks(batch);
ChunkUtils.optionalLoadAll(instance, affectedChunks, null).thenRun(() -> batch.apply(instance, onFinish)); ChunkUtils.optionalLoadAll(instance, affectedChunks, null).thenRun(() -> batch.apply(instance, b -> onFinish.run()));
} }
public static void loadAndApplyBatchBlocking(Batch batch, InstanceContainer instance) { public static void loadAndApplyBatchBlocking(AbsoluteBlockBatch batch, InstanceContainer instance) {
CompletableFuture<Void> future = new CompletableFuture<>(); CompletableFuture<Void> future = new CompletableFuture<>();
batch.awaitReady(); batch.awaitReady();
long[] affectedChunks = BatchUtil.getAffectedChunks(batch); long[] affectedChunks = BatchUtil.getAffectedChunks(batch);
CompletableFuture<Void> loadChunksTask = ChunkUtils.optionalLoadAll(instance, affectedChunks, null); CompletableFuture<Void> loadChunksTask = ChunkUtils.optionalLoadAll(instance, affectedChunks, null);
Runnable completerTask = () -> { loadChunksTask.thenRun(() -> batch.apply(instance, b -> future.complete(null)));
System.out.println("COMPLETE");
future.complete(null);
};
loadChunksTask.thenRun(() -> {
System.out.println("BEGIN");
batch.apply(instance, completerTask);
});
try { try {
future.get(); future.get();
@@ -33,9 +33,15 @@ public class Position {
public static List<Block> blocksBelowPlayer(Instance instance, Player p) { public static List<Block> blocksBelowPlayer(Instance instance, Player p) {
Point playerPos = p.getPosition(); Point playerPos = p.getPosition();
List<Block> blocks = new ArrayList<>(); List<Block> blocks = new ArrayList<>();
GeneratorUtils.foreachXZ(playerPos.sub(0.5, 1, 0.5), playerPos.add(0.5, -1, 0.5), point -> { GeneratorUtils.foreachXZ(
blocks.add(instance.getBlock(point)); playerPos.sub(0.5, 1, 0.5),
}); playerPos.add(0.5, -1, 0.5),
point -> blocks.add(instance.getBlock(point))
);
return blocks.stream().distinct().toList(); return blocks.stream().distinct().toList();
} }
public static boolean hasPositionChanged(Pos oldPos, Pos newPos) {
return !oldPos.withView(0, 0).equals(newPos.withView(0, 0));
}
} }
@@ -1,6 +1,7 @@
package eu.mhsl.minenet.minigames.world.generator.featureEnriched; package eu.mhsl.minenet.minigames.world.generator.featureEnriched;
import de.articdive.jnoise.JNoise; import de.articdive.jnoise.generators.noisegen.opensimplex.FastSimplexNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import eu.mhsl.minenet.minigames.util.NumberUtil; import eu.mhsl.minenet.minigames.util.NumberUtil;
import eu.mhsl.minenet.minigames.world.generator.terrain.HeightTerrainGenerator; import eu.mhsl.minenet.minigames.world.generator.terrain.HeightTerrainGenerator;
import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Point;
@@ -9,15 +10,13 @@ import java.util.function.Function;
public class ValeGenerator extends HeightTerrainGenerator { public class ValeGenerator extends HeightTerrainGenerator {
private final JNoise curves = JNoise.newBuilder() private final JNoise curves = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.01)
.setFrequency(0.01)
.build(); .build();
private final JNoise largeCurves = JNoise.newBuilder() private final JNoise largeCurves = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.001)
.setFrequency(0.001)
.build(); .build();
private Function<Integer, Double> xShiftMultiplier = multiplier -> 1d; private Function<Integer, Double> xShiftMultiplier = multiplier -> 1d;
@@ -33,7 +32,7 @@ public class ValeGenerator extends HeightTerrainGenerator {
} }
public int getXShiftAtZ(int z) { public int getXShiftAtZ(int z) {
return (int) ((this.curves.getNoise(z) * 32 + this.largeCurves.getNoise(z) * 64) * this.xShiftMultiplier.apply(z) + this.xShiftOffset.apply(z)); return (int) ((this.curves.evaluateNoise(z) * 32 + this.largeCurves.evaluateNoise(z) * 64) * this.xShiftMultiplier.apply(z) + this.xShiftOffset.apply(z));
} }
public void setXShiftMultiplier(Function<Integer, Double> xShiftMultiplier) { public void setXShiftMultiplier(Function<Integer, Double> xShiftMultiplier) {
@@ -1,6 +1,7 @@
package eu.mhsl.minenet.minigames.world.generator.terrain; package eu.mhsl.minenet.minigames.world.generator.terrain;
import de.articdive.jnoise.JNoise; import de.articdive.jnoise.generators.noisegen.opensimplex.FastSimplexNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import eu.mhsl.minenet.minigames.util.NumberUtil; import eu.mhsl.minenet.minigames.util.NumberUtil;
import eu.mhsl.minenet.minigames.world.BlockPallet; import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.PlateTerrainGenerator; import eu.mhsl.minenet.minigames.world.generator.PlateTerrainGenerator;
@@ -17,19 +18,16 @@ public class CircularPlateTerrainGenerator extends PlateTerrainGenerator {
protected final Random rnd = new Random(); protected final Random rnd = new Random();
private final int size; private final int size;
private final JNoise base = JNoise.newBuilder() private final JNoise base = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.01)
.setFrequency(0.01)
.build(); .build();
private final JNoise batches = JNoise.newBuilder() private final JNoise batches = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.05)
.setFrequency(0.05)
.build(); .build();
private final JNoise peaks = JNoise.newBuilder() private final JNoise peaks = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.1)
.setFrequency(0.1)
.build(); .build();
public CircularPlateTerrainGenerator(int size) { public CircularPlateTerrainGenerator(int size) {
@@ -61,11 +59,11 @@ public class CircularPlateTerrainGenerator extends PlateTerrainGenerator {
unit.modifier().setBlock(bottom, Block.GRASS_BLOCK); unit.modifier().setBlock(bottom, Block.GRASS_BLOCK);
synchronized(this.base) { synchronized(this.base) {
double baseNoise = this.base.getNoise(bottom.x(), bottom.z()); double baseNoise = this.base.evaluateNoise(bottom.x(), bottom.z());
double currentHeight = minTwo(NumberUtil.map(distance, 0, 400, -((double) this.size / 5), 200)) + baseNoise * 8; double currentHeight = minTwo(NumberUtil.map(distance, 0, 400, -((double) this.size / 5), 200)) + baseNoise * 8;
synchronized(this.batches) { synchronized(this.batches) {
double elementNoise = this.batches.getNoise(bottom.x(), bottom.z()); double elementNoise = this.batches.evaluateNoise(bottom.x(), bottom.z());
unit.modifier().fill( unit.modifier().fill(
bottom, bottom,
@@ -76,7 +74,7 @@ public class CircularPlateTerrainGenerator extends PlateTerrainGenerator {
} }
synchronized(this.peaks) { synchronized(this.peaks) {
double peakNoise = this.peaks.getNoise(bottom.x(), bottom.z()); double peakNoise = this.peaks.evaluateNoise(bottom.x(), bottom.z());
if(peakNoise > 0.97 && bottom.distance(new Pos(0, 0, 0)) > (this.size + 20)) { if(peakNoise > 0.97 && bottom.distance(new Pos(0, 0, 0)) > (this.size + 20)) {
@@ -1,6 +1,7 @@
package eu.mhsl.minenet.minigames.world.generator.terrain; package eu.mhsl.minenet.minigames.world.generator.terrain;
import de.articdive.jnoise.JNoise; import de.articdive.jnoise.generators.noisegen.opensimplex.FastSimplexNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import eu.mhsl.minenet.minigames.util.GeneratorUtils; import eu.mhsl.minenet.minigames.util.GeneratorUtils;
import eu.mhsl.minenet.minigames.util.NumberUtil; import eu.mhsl.minenet.minigames.util.NumberUtil;
import eu.mhsl.minenet.minigames.world.BlockPallet; import eu.mhsl.minenet.minigames.world.BlockPallet;
@@ -17,19 +18,16 @@ import java.util.function.Function;
public class HeightTerrainGenerator extends BaseGenerator { public class HeightTerrainGenerator extends BaseGenerator {
protected final Random rnd = new Random(); protected final Random rnd = new Random();
private final JNoise base = JNoise.newBuilder() private final JNoise base = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.01)
.setFrequency(0.01)
.build(); .build();
private final JNoise batches = JNoise.newBuilder() private final JNoise batches = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.05)
.setFrequency(0.05)
.build(); .build();
private final JNoise peaks = JNoise.newBuilder() private final JNoise peaks = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.1)
.setFrequency(0.1)
.build(); .build();
private final BlockPallet blockPallet = BlockPallet.STONE; private final BlockPallet blockPallet = BlockPallet.STONE;
protected Function<Point, Integer> heightNoiseMultiplier = (Point) -> 16; protected Function<Point, Integer> heightNoiseMultiplier = (Point) -> 16;
@@ -43,13 +41,13 @@ public class HeightTerrainGenerator extends BaseGenerator {
unit.modifier().setBlock(bottomPoint, this.blockPallet.rnd()); unit.modifier().setBlock(bottomPoint, this.blockPallet.rnd());
synchronized(this.base) { synchronized(this.base) {
double heightNoise = this.base.getNoise(bottomPoint.x(), bottomPoint.z()); double heightNoise = this.base.evaluateNoise(bottomPoint.x(), bottomPoint.z());
double noiseModifier = heightNoise * this.heightNoiseMultiplier.apply(bottomPoint); double noiseModifier = heightNoise * this.heightNoiseMultiplier.apply(bottomPoint);
double heightModifier = NumberUtil.clamp(this.calculateHeight.apply(bottomPoint) + noiseModifier, 1d, unit.size().y()); double heightModifier = NumberUtil.clamp(this.calculateHeight.apply(bottomPoint) + noiseModifier, 1d, unit.size().y());
if(heightModifier < 1) System.out.println("HEIGHT MODIFIER ILLEGAL"); if(heightModifier < 1) System.out.println("HEIGHT MODIFIER ILLEGAL");
synchronized(this.batches) { synchronized(this.batches) {
double batchNoise = this.batches.getNoise(bottomPoint.x(), bottomPoint.z()); double batchNoise = this.batches.evaluateNoise(bottomPoint.x(), bottomPoint.z());
Block block = batchNoise < 0.9 ? batchNoise > -0.2 ? Block.GRASS_BLOCK : Block.SOUL_SAND : Block.STONE; Block block = batchNoise < 0.9 ? batchNoise > -0.2 ? Block.GRASS_BLOCK : Block.SOUL_SAND : Block.STONE;
unit.modifier().fill(bottomPoint, bottomPoint.add(1, heightModifier, 1), block); unit.modifier().fill(bottomPoint, bottomPoint.add(1, heightModifier, 1), block);
@@ -77,7 +75,7 @@ public class HeightTerrainGenerator extends BaseGenerator {
} }
synchronized(this.peaks) { synchronized(this.peaks) {
double peakNoise = this.peaks.getNoise(bottomPoint.x(), bottomPoint.z()); double peakNoise = this.peaks.evaluateNoise(bottomPoint.x(), bottomPoint.z());
if(peakNoise > 0.97 && bottomPoint.distance(new Pos(0, 0, 0)) > (50 + 20)) { if(peakNoise > 0.97 && bottomPoint.distance(new Pos(0, 0, 0)) > (50 + 20)) {
@@ -1,6 +1,7 @@
package eu.mhsl.minenet.minigames.world.generator.terrain; package eu.mhsl.minenet.minigames.world.generator.terrain;
import de.articdive.jnoise.JNoise; import de.articdive.jnoise.generators.noisegen.opensimplex.FastSimplexNoiseGenerator;
import de.articdive.jnoise.pipeline.JNoise;
import eu.mhsl.minenet.minigames.util.NumberUtil; import eu.mhsl.minenet.minigames.util.NumberUtil;
import eu.mhsl.minenet.minigames.world.BlockPallet; import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.PlateTerrainGenerator; import eu.mhsl.minenet.minigames.world.generator.PlateTerrainGenerator;
@@ -21,19 +22,16 @@ public class SquarePlateTerrainGenerator extends PlateTerrainGenerator {
private final int width; private final int width;
private final int length; private final int length;
private final JNoise base = JNoise.newBuilder() private final JNoise base = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.01)
.setFrequency(0.01)
.build(); .build();
private final JNoise batches = JNoise.newBuilder() private final JNoise batches = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.05)
.setFrequency(0.05)
.build(); .build();
private final JNoise peaks = JNoise.newBuilder() private final JNoise peaks = JNoise.newBuilder()
.fastSimplex() .fastSimplex(FastSimplexNoiseGenerator.newBuilder().setSeed(this.rnd.nextLong()).build())
.setSeed(this.rnd.nextLong()) .scale(0.1)
.setFrequency(0.1)
.build(); .build();
public SquarePlateTerrainGenerator(int width, int length) { public SquarePlateTerrainGenerator(int width, int length) {
@@ -82,7 +80,7 @@ public class SquarePlateTerrainGenerator extends PlateTerrainGenerator {
unit.modifier().setBlock(bottom, Block.GRASS_BLOCK); unit.modifier().setBlock(bottom, Block.GRASS_BLOCK);
synchronized(this.base) { synchronized(this.base) {
double baseNoise = this.base.getNoise(bottom.x(), bottom.z()); double baseNoise = this.base.evaluateNoise(bottom.x(), bottom.z());
double[] possibleHeights = { double[] possibleHeights = {
minTwo(NumberUtil.map(bottom.distance(new Pos(0, 0, 0)), 0, 400, -((double) this.width / 5), 200)) + baseNoise * 8, minTwo(NumberUtil.map(bottom.distance(new Pos(0, 0, 0)), 0, 400, -((double) this.width / 5), 200)) + baseNoise * 8,
minTwo(NumberUtil.map(bottom.distance(new Pos(this.width, 0, 0)), 0, 400, -((double) this.width / 5), 200)) + baseNoise * 8, minTwo(NumberUtil.map(bottom.distance(new Pos(this.width, 0, 0)), 0, 400, -((double) this.width / 5), 200)) + baseNoise * 8,
@@ -93,7 +91,7 @@ public class SquarePlateTerrainGenerator extends PlateTerrainGenerator {
double currentHeight = Arrays.stream(possibleHeights).min().getAsDouble(); double currentHeight = Arrays.stream(possibleHeights).min().getAsDouble();
synchronized(this.batches) { synchronized(this.batches) {
double elementNoise = this.batches.getNoise(bottom.x(), bottom.z()); double elementNoise = this.batches.evaluateNoise(bottom.x(), bottom.z());
unit.modifier().fill( unit.modifier().fill(
bottom, bottom,
@@ -104,7 +102,7 @@ public class SquarePlateTerrainGenerator extends PlateTerrainGenerator {
} }
synchronized(this.peaks) { synchronized(this.peaks) {
double peakNoise = this.peaks.getNoise(bottom.x(), bottom.z()); double peakNoise = this.peaks.evaluateNoise(bottom.x(), bottom.z());
if(peakNoise > 0.97 && bottom.distance(new Pos(0, 0, 0)) > (this.width + 20)) { if(peakNoise > 0.97 && bottom.distance(new Pos(0, 0, 0)) > (this.width + 20)) {
+17
View File
@@ -174,3 +174,20 @@ startSpeed;Start Speed;Startgeschwindigkeit
ns:game_BoatRace#;; ns:game_BoatRace#;;
name;Boatrace;Bootrennen name;Boatrace;Bootrennen
description;; description;;
;;
ns:game_Pillars#;;
name;Pillars;Pillars
description;Build yourself up with your random blocks to reach your opponents and push them down!;Baue dich mit deinen zufälligen Blöcken zu deinen Gegnern und schupse sie runter!
;;
ns:game_BlockBattle#;;
name;Block Battle;Block Kampf
description;The team that fills the center with their color first wins!;Das Team, welches als erstes die Mitte mit seiner Farbe gefüllt hat, gewinnt!
itemCount;Block Count;Block Anzahl
;;
ns:game_ColorJump#;;
name;Color Jump;Farbsprung
description;Jump on the right color!;Springe auf die richtige Farbe!
;;
ns:game_PlayerRace#;;
name;Player Race;Wettrennen
description;Get to the other side as quickly as possible!;Komme so schnell wie möglich auf die andere Seite!
1 map en_us de_de
174 ns:game_BoatRace#
175 name Boatrace Bootrennen
176 description
177
178 ns:game_Pillars#
179 name Pillars Pillars
180 description Build yourself up with your random blocks to reach your opponents and push them down! Baue dich mit deinen zufälligen Blöcken zu deinen Gegnern und schupse sie runter!
181
182 ns:game_BlockBattle#
183 name Block Battle Block Kampf
184 description The team that fills the center with their color first wins! Das Team, welches als erstes die Mitte mit seiner Farbe gefüllt hat, gewinnt!
185 itemCount Block Count Block Anzahl
186
187 ns:game_ColorJump#
188 name Color Jump Farbsprung
189 description Jump on the right color! Springe auf die richtige Farbe!
190
191 ns:game_PlayerRace#
192 name Player Race Wettrennen
193 description Get to the other side as quickly as possible! Komme so schnell wie möglich auf die andere Seite!