├── .github
└── workflows
│ └── prerelease.yml
├── .gitignore
├── .idea
├── .gitignore
├── gradle.xml
├── misc.xml
├── uiDesigner.xml
└── vcs.xml
├── LICENSE
├── README.md
├── build.gradle.kts
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── resourcepack
├── assets.ajmeta
├── assets
│ ├── animated_java
│ │ ├── items
│ │ │ └── blueprint
│ │ │ │ └── player_display
│ │ │ │ ├── head.json
│ │ │ │ ├── left_arm.json
│ │ │ │ ├── left_leg.json
│ │ │ │ ├── right_arm.json
│ │ │ │ ├── right_leg.json
│ │ │ │ └── torso.json
│ │ └── models
│ │ │ └── blueprint
│ │ │ └── player_display
│ │ │ ├── head.json
│ │ │ ├── left_arm.json
│ │ │ ├── left_leg.json
│ │ │ ├── right_arm.json
│ │ │ ├── right_leg.json
│ │ │ ├── slim
│ │ │ ├── head.json
│ │ │ ├── left_arm.json
│ │ │ ├── left_leg.json
│ │ │ ├── right_arm.json
│ │ │ ├── right_leg.json
│ │ │ └── torso.json
│ │ │ └── torso.json
│ └── minecraft
│ │ └── shaders
│ │ ├── core
│ │ └── rendertype_entity_translucent.json
│ │ ├── rendertype_entity_translucent.fsh
│ │ └── rendertype_entity_translucent.vsh
└── pack.mcmeta
├── settings.gradle.kts
└── src
└── main
└── java
└── dev
└── emortal
├── Main.java
├── MinecraftPhysics.java
├── NoTickingEntity.java
├── PlayerDisplayPart.java
├── SphereUtil.java
├── WorldBlock.java
├── commands
├── ChainLengthCommand.java
├── ClearCommand.java
├── PerformanceCommand.java
├── PlayerSizeCommand.java
└── TntStrengthCommand.java
├── objects
├── BlockRigidBody.java
├── ChainPhysics.java
├── LanternPhysics.java
├── MinecraftPhysicsObject.java
├── RagdollPhysics.java
└── TrapdoorPhysics.java
├── tools
├── DeleteTool.java
├── DiamondLayerTool.java
├── GrabberTool.java
├── PlayerSpawnerTool.java
├── Tool.java
└── WeldTool.java
└── utils
├── CoordinateUtils.java
└── QuaternionCustom.java
/.github/workflows/prerelease.yml:
--------------------------------------------------------------------------------
1 | name: "pre-release"
2 |
3 | on:
4 | push:
5 | branches:
6 | - "main"
7 |
8 | jobs:
9 | pre-release:
10 | name: "Pre Release"
11 | runs-on: "ubuntu-latest"
12 |
13 | steps:
14 | - uses: actions/checkout@v3
15 | - name: Set up Java 21
16 | uses: actions/setup-java@v3
17 | with:
18 | java-version: 21
19 | distribution: 'zulu'
20 | cache: 'gradle'
21 | - name: Grant execute permission for gradlew
22 | run: chmod +x gradlew
23 | - name: Build with Gradle
24 | run: ./gradlew build
25 | - uses: "marvinpinto/action-automatic-releases@latest"
26 | with:
27 | repo_token: "${{ secrets.GITHUB_TOKEN }}"
28 | automatic_release_tag: "latest"
29 | prerelease: true
30 | title: "Development Build"
31 | files: |
32 | LICENSE.md
33 | build/libs/*.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | build/
3 | !gradle/wrapper/gradle-wrapper.jar
4 | !**/src/main/**/build/
5 | !**/src/test/**/build/
6 |
7 | ### IntelliJ IDEA ###
8 | .idea/modules.xml
9 | .idea/jarRepositories.xml
10 | .idea/compiler.xml
11 | .idea/libraries/
12 | *.iws
13 | *.iml
14 | *.ipr
15 | out/
16 | !**/src/main/**/out/
17 | !**/src/test/**/out/
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/uiDesigner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | -
6 |
7 |
8 | -
9 |
10 |
11 | -
12 |
13 |
14 | -
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 | -
24 |
25 |
26 |
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
35 | -
36 |
37 |
38 |
39 |
40 |
41 | -
42 |
43 |
44 |
45 |
46 | -
47 |
48 |
49 |
50 |
51 | -
52 |
53 |
54 |
55 |
56 | -
57 |
58 |
59 |
60 |
61 | -
62 |
63 |
64 |
65 |
66 | -
67 |
68 |
69 |
70 |
71 | -
72 |
73 |
74 | -
75 |
76 |
77 |
78 |
79 | -
80 |
81 |
82 |
83 |
84 | -
85 |
86 |
87 |
88 |
89 | -
90 |
91 |
92 |
93 |
94 | -
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | -
103 |
104 |
105 | -
106 |
107 |
108 | -
109 |
110 |
111 | -
112 |
113 |
114 |
115 |
116 | -
117 |
118 |
119 | -
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 emortaldev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BlockPhysics
2 | A Minecraft physics playground made with [LibGDX Bullet](https://libgdx.com/wiki/extensions/physics/bullet/bullet-physics) and [Minestom](https://github.com/Minestom/Minestom)
3 |
4 | https://www.youtube.com/watch?v=PrgzoKWOCuQ
5 |
6 | Consider checking out the [PhysX branch](https://github.com/emortaldev/BlockPhysics/tree/physx)
7 |
8 | ## Setup
9 | - Download latest jar [here](https://github.com/emortaldev/BlockPhysics/releases/download/latest/BlockPhysics-1.0.0-all.jar)
10 | - Run `java -Xmx1G -jar BlockPhysics-1.0.0-all.jar`
11 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
2 |
3 | plugins {
4 | java
5 | id("com.github.johnrengelman.shadow") version "8.1.1"
6 | }
7 |
8 | group = "dev.emortal"
9 | version = "1.0.0"
10 |
11 | repositories {
12 | mavenCentral()
13 | maven(url = "https://jitpack.io")
14 | maven {
15 | url = uri("https://repo.emortal.dev/snapshots")
16 | }
17 | }
18 |
19 | dependencies {
20 | implementation("com.github.stephengold:Libbulletjme-Windows64:22.0.1")
21 |
22 | // native libraries:
23 | runtimeOnly("com.github.stephengold:Libbulletjme-Linux64:22.0.1:SpDebug")
24 |
25 | implementation("io.github.electrostat-lab:snaploader:1.0.0-stable")
26 |
27 | implementation("net.minestom:minestom-snapshots:1_21_4-44b34717ed")
28 |
29 | implementation("ch.qos.logback:logback-classic:1.5.17")
30 | }
31 |
32 | tasks {
33 | named("shadowJar") {
34 | archiveBaseName.set("BlockPhysics")
35 | mergeServiceFiles()
36 | manifest {
37 | attributes(mapOf("Main-Class" to "dev.emortal.Main"))
38 | }
39 | }
40 | }
41 |
42 | tasks {
43 | build {
44 | dependsOn(shadowJar)
45 | }
46 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/emortaldev/BlockPhysics/2a7e5e63e5b882985084a0d5d28cf339f2f7dfd6/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Oct 01 21:33:41 BST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84 |
85 | APP_NAME="Gradle"
86 | APP_BASE_NAME=${0##*/}
87 |
88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90 |
91 | # Use the maximum available, or set MAX_FD != -1 to use that value.
92 | MAX_FD=maximum
93 |
94 | warn () {
95 | echo "$*"
96 | } >&2
97 |
98 | die () {
99 | echo
100 | echo "$*"
101 | echo
102 | exit 1
103 | } >&2
104 |
105 | # OS specific support (must be 'true' or 'false').
106 | cygwin=false
107 | msys=false
108 | darwin=false
109 | nonstop=false
110 | case "$( uname )" in #(
111 | CYGWIN* ) cygwin=true ;; #(
112 | Darwin* ) darwin=true ;; #(
113 | MSYS* | MINGW* ) msys=true ;; #(
114 | NONSTOP* ) nonstop=true ;;
115 | esac
116 |
117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118 |
119 |
120 | # Determine the Java command to use to start the JVM.
121 | if [ -n "$JAVA_HOME" ] ; then
122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123 | # IBM's JDK on AIX uses strange locations for the executables
124 | JAVACMD=$JAVA_HOME/jre/sh/java
125 | else
126 | JAVACMD=$JAVA_HOME/bin/java
127 | fi
128 | if [ ! -x "$JAVACMD" ] ; then
129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130 |
131 | Please set the JAVA_HOME variable in your environment to match the
132 | location of your Java installation."
133 | fi
134 | else
135 | JAVACMD=java
136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 |
142 | # Increase the maximum file descriptors if we can.
143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144 | case $MAX_FD in #(
145 | max*)
146 | MAX_FD=$( ulimit -H -n ) ||
147 | warn "Could not query maximum file descriptor limit"
148 | esac
149 | case $MAX_FD in #(
150 | '' | soft) :;; #(
151 | *)
152 | ulimit -n "$MAX_FD" ||
153 | warn "Could not set maximum file descriptor limit to $MAX_FD"
154 | esac
155 | fi
156 |
157 | # Collect all arguments for the java command, stacking in reverse order:
158 | # * args from the command line
159 | # * the main class name
160 | # * -classpath
161 | # * -D...appname settings
162 | # * --module-path (only if needed)
163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164 |
165 | # For Cygwin or MSYS, switch paths to Windows format before running java
166 | if "$cygwin" || "$msys" ; then
167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169 |
170 | JAVACMD=$( cygpath --unix "$JAVACMD" )
171 |
172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
173 | for arg do
174 | if
175 | case $arg in #(
176 | -*) false ;; # don't mess with options #(
177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
178 | [ -e "$t" ] ;; #(
179 | *) false ;;
180 | esac
181 | then
182 | arg=$( cygpath --path --ignore --mixed "$arg" )
183 | fi
184 | # Roll the args list around exactly as many times as the number of
185 | # args, so each arg winds up back in the position where it started, but
186 | # possibly modified.
187 | #
188 | # NB: a `for` loop captures its iteration list before it begins, so
189 | # changing the positional parameters here affects neither the number of
190 | # iterations, nor the values presented in `arg`.
191 | shift # remove old arg
192 | set -- "$@" "$arg" # push replacement arg
193 | done
194 | fi
195 |
196 | # Collect all arguments for the java command;
197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198 | # shell script including quotes and variable substitutions, so put them in
199 | # double quotes to make sure that they get re-expanded; and
200 | # * put everything else in single quotes, so that it's not re-expanded.
201 |
202 | set -- \
203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
204 | -classpath "$CLASSPATH" \
205 | org.gradle.wrapper.GradleWrapperMain \
206 | "$@"
207 |
208 | # Use "xargs" to parse quoted args.
209 | #
210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211 | #
212 | # In Bash we could simply go:
213 | #
214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215 | # set -- "${ARGS[@]}" "$@"
216 | #
217 | # but POSIX shell has neither arrays nor command substitution, so instead we
218 | # post-process each arg (as a line of input to sed) to backslash-escape any
219 | # character that might be a shell metacharacter, then use eval to reverse
220 | # that process (while maintaining the separation between arguments), and wrap
221 | # the whole thing up as a single "set" statement.
222 | #
223 | # This will of course break if any of these variables contains a newline or
224 | # an unmatched quote.
225 | #
226 |
227 | eval "set -- $(
228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229 | xargs -n1 |
230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231 | tr '\n' ' '
232 | )" '"$@"'
233 |
234 | exec "$JAVACMD" "$@"
235 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/resourcepack/assets.ajmeta:
--------------------------------------------------------------------------------
1 | {
2 | "player_display": {
3 | "files": [
4 | "assets/animated_java/models/empty.json",
5 | "assets/animated_java/textures/blueprint/player_display/skin.png",
6 | "assets/animated_java/items/blueprint/player_display/torso.json",
7 | "assets/animated_java/items/blueprint/player_display/head.json",
8 | "assets/animated_java/items/blueprint/player_display/right_arm.json",
9 | "assets/animated_java/items/blueprint/player_display/left_arm.json",
10 | "assets/animated_java/items/blueprint/player_display/right_leg.json",
11 | "assets/animated_java/items/blueprint/player_display/left_leg.json",
12 | "assets/animated_java/models/blueprint/player_display/torso.json",
13 | "assets/animated_java/models/blueprint/player_display/head.json",
14 | "assets/animated_java/models/blueprint/player_display/right_arm.json",
15 | "assets/animated_java/models/blueprint/player_display/left_arm.json",
16 | "assets/animated_java/models/blueprint/player_display/right_leg.json",
17 | "assets/animated_java/models/blueprint/player_display/left_leg.json",
18 | "assets/animated_java/models/blueprint/player_display/slim/head.json",
19 | "assets/animated_java/models/blueprint/player_display/slim/right_arm.json",
20 | "assets/animated_java/models/blueprint/player_display/slim/left_arm.json",
21 | "assets/animated_java/models/blueprint/player_display/slim/right_leg.json",
22 | "assets/animated_java/models/blueprint/player_display/slim/left_leg.json",
23 | "assets/animated_java/models/blueprint/player_display/slim/torso.json"
24 | ]
25 | }
26 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/items/blueprint/player_display/head.json:
--------------------------------------------------------------------------------
1 | {
2 | "model": {
3 | "type": "minecraft:select",
4 | "property": "minecraft:custom_model_data",
5 | "cases": [
6 | {
7 | "when": "slim",
8 | "model": {
9 | "type": "minecraft:special",
10 | "base": "animated_java:blueprint/player_display/slim/head",
11 | "model": {
12 | "type": "minecraft:head",
13 | "kind": "player"
14 | }
15 | }
16 | }
17 | ],
18 | "fallback": {
19 | "type": "minecraft:special",
20 | "base": "animated_java:blueprint/player_display/head",
21 | "model": {
22 | "type": "minecraft:head",
23 | "kind": "player"
24 | }
25 | },
26 | "tints": [
27 | {
28 | "type": "minecraft:dye",
29 | "default": [1, 1, 1]
30 | }
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/items/blueprint/player_display/left_arm.json:
--------------------------------------------------------------------------------
1 | {
2 | "model": {
3 | "type": "minecraft:select",
4 | "property": "minecraft:custom_model_data",
5 | "cases": [
6 | {
7 | "when": "slim",
8 | "model": {
9 | "type": "minecraft:special",
10 | "base": "animated_java:blueprint/player_display/slim/left_arm",
11 | "model": {
12 | "type": "minecraft:head",
13 | "kind": "player"
14 | }
15 | }
16 | }
17 | ],
18 | "fallback": {
19 | "type": "minecraft:special",
20 | "base": "animated_java:blueprint/player_display/left_arm",
21 | "model": {
22 | "type": "minecraft:head",
23 | "kind": "player"
24 | }
25 | },
26 | "tints": [
27 | {
28 | "type": "minecraft:dye",
29 | "default": [1, 1, 1]
30 | }
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/items/blueprint/player_display/left_leg.json:
--------------------------------------------------------------------------------
1 | {
2 | "model": {
3 | "type": "minecraft:select",
4 | "property": "minecraft:custom_model_data",
5 | "cases": [
6 | {
7 | "when": "slim",
8 | "model": {
9 | "type": "minecraft:special",
10 | "base": "animated_java:blueprint/player_display/slim/left_leg",
11 | "model": {
12 | "type": "minecraft:head",
13 | "kind": "player"
14 | }
15 | }
16 | }
17 | ],
18 | "fallback": {
19 | "type": "minecraft:special",
20 | "base": "animated_java:blueprint/player_display/left_leg",
21 | "model": {
22 | "type": "minecraft:head",
23 | "kind": "player"
24 | }
25 | },
26 | "tints": [
27 | {
28 | "type": "minecraft:dye",
29 | "default": [1, 1, 1]
30 | }
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/items/blueprint/player_display/right_arm.json:
--------------------------------------------------------------------------------
1 | {
2 | "model": {
3 | "type": "minecraft:select",
4 | "property": "minecraft:custom_model_data",
5 | "cases": [
6 | {
7 | "when": "slim",
8 | "model": {
9 | "type": "minecraft:special",
10 | "base": "animated_java:blueprint/player_display/slim/right_arm",
11 | "model": {
12 | "type": "minecraft:head",
13 | "kind": "player"
14 | }
15 | }
16 | }
17 | ],
18 | "fallback": {
19 | "type": "minecraft:special",
20 | "base": "animated_java:blueprint/player_display/right_arm",
21 | "model": {
22 | "type": "minecraft:head",
23 | "kind": "player"
24 | }
25 | },
26 | "tints": [
27 | {
28 | "type": "minecraft:dye",
29 | "default": [1, 1, 1]
30 | }
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/items/blueprint/player_display/right_leg.json:
--------------------------------------------------------------------------------
1 | {
2 | "model": {
3 | "type": "minecraft:select",
4 | "property": "minecraft:custom_model_data",
5 | "cases": [
6 | {
7 | "when": "slim",
8 | "model": {
9 | "type": "minecraft:special",
10 | "base": "animated_java:blueprint/player_display/slim/right_leg",
11 | "model": {
12 | "type": "minecraft:head",
13 | "kind": "player"
14 | }
15 | }
16 | }
17 | ],
18 | "fallback": {
19 | "type": "minecraft:special",
20 | "base": "animated_java:blueprint/player_display/right_leg",
21 | "model": {
22 | "type": "minecraft:head",
23 | "kind": "player"
24 | }
25 | },
26 | "tints": [
27 | {
28 | "type": "minecraft:dye",
29 | "default": [1, 1, 1]
30 | }
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/items/blueprint/player_display/torso.json:
--------------------------------------------------------------------------------
1 | {
2 | "model": {
3 | "type": "minecraft:select",
4 | "property": "minecraft:custom_model_data",
5 | "cases": [
6 | {
7 | "when": "slim",
8 | "model": {
9 | "type": "minecraft:special",
10 | "base": "animated_java:blueprint/player_display/slim/torso",
11 | "model": {
12 | "type": "minecraft:head",
13 | "kind": "player"
14 | }
15 | }
16 | }
17 | ],
18 | "fallback": {
19 | "type": "minecraft:special",
20 | "base": "animated_java:blueprint/player_display/torso",
21 | "model": {
22 | "type": "minecraft:head",
23 | "kind": "player"
24 | }
25 | },
26 | "tints": [
27 | {
28 | "type": "minecraft:dye",
29 | "default": [1, 1, 1]
30 | }
31 | ]
32 | }
33 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/head.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[0,3.75,0],"scale":[0.9375,0.9375,0.9375]}}}
2 |
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/left_arm.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[0,5.62500,0],"scale":[0.46875,1.40625,0.46875]}}}
2 |
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/left_leg.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[0,5.62500,0],"scale":[0.46875,1.40625,0.46875]}}}
2 |
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/right_arm.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[0,5.62500,0],"scale":[0.46875,1.40625,0.46875]}}}
2 |
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/right_leg.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[0,5.62500,0],"scale":[0.46875,1.40625,0.46875]}}}
2 |
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/slim/head.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "animated_java:blueprint/player_display/head"
3 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/slim/left_arm.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[1.1575,0,0],"scale":[0.3515625,1.40625,0.46875]}}}
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/slim/left_leg.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "animated_java:blueprint/player_display/left_leg"
3 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/slim/right_arm.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[-1.1575,0,0],"scale":[0.3515625,1.40625,0.46875]}}}
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/slim/right_leg.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "animated_java:blueprint/player_display/right_leg"
3 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/slim/torso.json:
--------------------------------------------------------------------------------
1 | {
2 | "parent": "animated_java:blueprint/player_display/torso"
3 | }
--------------------------------------------------------------------------------
/resourcepack/assets/animated_java/models/blueprint/player_display/torso.json:
--------------------------------------------------------------------------------
1 | {"display":{"thirdperson_righthand":{"rotation":[0,0,0],"translation":[0,5.62500,0],"scale":[0.9375,1.40625,0.46875]}}}
2 |
--------------------------------------------------------------------------------
/resourcepack/assets/minecraft/shaders/core/rendertype_entity_translucent.json:
--------------------------------------------------------------------------------
1 | {
2 | "blend": {
3 | "func": "add",
4 | "srcrgb": "srcalpha",
5 | "dstrgb": "1-srcalpha"
6 | },
7 | "vertex": "rendertype_entity_translucent",
8 | "fragment": "rendertype_entity_translucent",
9 | "attributes": [
10 | "Position",
11 | "Color",
12 | "UV0",
13 | "UV1",
14 | "UV2",
15 | "Normal"
16 | ],
17 | "samplers": [
18 | { "name": "Sampler0" },
19 | { "name": "Sampler1" },
20 | { "name": "Sampler2" }
21 | ],
22 | "uniforms": [
23 | { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
24 | { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
25 | { "name": "IViewRotMat", "type": "matrix3x3", "count": 9, "values": [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] },
26 | { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
27 | { "name": "Light0_Direction", "type": "float", "count": 3, "values": [ 0.0, 0.0, 0.0 ] },
28 | { "name": "Light1_Direction", "type": "float", "count": 3, "values": [ 0.0, 0.0, 0.0 ] },
29 | { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },
30 | { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },
31 | { "name": "FogColor", "type": "float", "count": 4,"values": [ 0.0, 0.0, 0.0, 0.0 ]},
32 | { "name": "FogShape", "type": "int", "count": 1, "values": [ 0 ] }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/resourcepack/assets/minecraft/shaders/rendertype_entity_translucent.fsh:
--------------------------------------------------------------------------------
1 | #version 150
2 |
3 | #moj_import
4 |
5 | uniform sampler2D Sampler0;
6 |
7 | uniform mat4 ModelViewMat;
8 | uniform mat4 ProjMat;
9 | uniform mat3 IViewRotMat;
10 |
11 | uniform vec4 ColorModulator;
12 | uniform float FogStart;
13 | uniform float FogEnd;
14 | uniform vec4 FogColor;
15 |
16 | in float vertexDistance;
17 | in vec4 vertexColor;
18 | in vec4 lightMapColor;
19 | in vec4 overlayColor;
20 | in vec2 texCoord0;
21 | in vec2 texCoord1;
22 | in vec4 normal;
23 | in float part;
24 |
25 | out vec4 fragColor;
26 |
27 | void main() {
28 | vec4 color = texture(Sampler0, texCoord0);
29 | if (color.a < 0.1 || abs(mod(part + 0.5, 1.0) - 0.5) > 0.001) {
30 | discard;
31 | }
32 | if (color.a < 1.0 && part > 0.5) {
33 | vec4 color2 = texture(Sampler0, texCoord1);
34 | if (color.a < 0.75 && int(gl_FragCoord.x + gl_FragCoord.y) % 2 == 0) {
35 | discard;
36 | }
37 | else {
38 | color.rgb = mix(color2.rgb, color.rgb, min(1.0, color.a * 2));
39 | color.a = 1.0;
40 | }
41 | }
42 |
43 | color *= vertexColor * ColorModulator;
44 | color.rgb = mix(overlayColor.rgb, color.rgb, overlayColor.a);
45 | color *= lightMapColor;
46 | fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor);
47 | }
48 |
--------------------------------------------------------------------------------
/resourcepack/assets/minecraft/shaders/rendertype_entity_translucent.vsh:
--------------------------------------------------------------------------------
1 | #version 150
2 |
3 | #moj_import
4 | #moj_import
5 |
6 | in vec3 Position;
7 | in vec4 Color;
8 | in vec2 UV0;
9 | in ivec2 UV1;
10 | in ivec2 UV2;
11 | in vec3 Normal;
12 |
13 | uniform sampler2D Sampler0;
14 | uniform sampler2D Sampler1;
15 | uniform sampler2D Sampler2;
16 |
17 | uniform mat4 ModelViewMat;
18 | uniform mat4 ProjMat;
19 | uniform mat3 IViewRotMat;
20 |
21 | uniform vec3 Light0_Direction;
22 | uniform vec3 Light1_Direction;
23 |
24 | uniform int FogShape;
25 |
26 | out float vertexDistance;
27 | out vec4 vertexColor;
28 | out vec4 lightMapColor;
29 | out vec4 overlayColor;
30 | out vec2 texCoord0;
31 | out vec2 texCoord1;
32 | out vec4 normal;
33 | out float part;
34 |
35 | #define SPACING 1024.0
36 | #define MAXRANGE (0.5 * SPACING)
37 | #define SKINRES 64
38 | #define FACERES 8
39 |
40 | const vec4[] subuvs = vec4[](
41 | vec4(4.0, 0.0, 8.0, 4.0 ), // 4x4x12
42 | vec4(8.0, 0.0, 12.0, 4.0 ),
43 | vec4(0.0, 4.0, 4.0, 16.0),
44 | vec4(4.0, 4.0, 8.0, 16.0),
45 | vec4(8.0, 4.0, 12.0, 16.0),
46 | vec4(12.0, 4.0, 16.0, 16.0),
47 | vec4(4.0, 0.0, 7.0, 4.0 ), // 4x3x12
48 | vec4(7.0, 0.0, 10.0, 4.0 ),
49 | vec4(0.0, 4.0, 4.0, 16.0),
50 | vec4(4.0, 4.0, 7.0, 16.0),
51 | vec4(7.0, 4.0, 11.0, 16.0),
52 | vec4(11.0, 4.0, 14.0, 16.0),
53 | vec4(4.0, 0.0, 12.0, 4.0 ), // 4x8x12
54 | vec4(12.0, 0.0, 20.0, 4.0 ),
55 | vec4(0.0, 4.0, 4.0, 16.0),
56 | vec4(4.0, 4.0, 12.0, 16.0),
57 | vec4(12.0, 4.0, 16.0, 16.0),
58 | vec4(16.0, 4.0, 24.0, 16.0)
59 | );
60 |
61 | const vec2[] origins = vec2[](
62 | vec2(40.0, 16.0), // right arm
63 | vec2(40.0, 32.0),
64 | vec2(32.0, 48.0), // left arm
65 | vec2(48.0, 48.0),
66 | vec2(16.0, 16.0), // torso
67 | vec2(16.0, 32.0),
68 | vec2(0.0, 16.0), // right leg
69 | vec2(0.0, 32.0),
70 | vec2(16.0, 48.0), // left leg
71 | vec2(0.0, 48.0)
72 | );
73 |
74 | const int[] faceremap = int[](0, 0, 1, 1, 2, 3, 4, 5);
75 |
76 | void main() {
77 | vertexColor = minecraft_mix_light(Light0_Direction, Light1_Direction, normalize(Normal), Color);
78 | lightMapColor = texelFetch(Sampler2, UV2 / 16, 0);
79 | overlayColor = texelFetch(Sampler1, UV1, 0);
80 | normal = ProjMat * ModelViewMat * vec4(Normal, 0.0);
81 |
82 | ivec2 dim = textureSize(Sampler0, 0);
83 |
84 | if (ProjMat[2][3] == 0.0 || dim.x != 64 || dim.y != 64) { // short circuit if cannot be player
85 | part = 0.0;
86 | texCoord0 = UV0;
87 | texCoord1 = vec2(0.0);
88 | vertexDistance = fog_distance(IViewRotMat * Position, FogShape);
89 | gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0);
90 | }
91 | else {
92 | vec3 wpos = IViewRotMat * Position;
93 | vec2 UVout = UV0;
94 | vec2 UVout2 = vec2(0.0);
95 | int partId = -int((wpos.y - MAXRANGE) / SPACING);
96 |
97 | part = float(partId);
98 |
99 | if (partId == 0) { // higher precision position if no translation is needed
100 | gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0);
101 | }
102 | else {
103 | vec4 samp1 = texture(Sampler0, vec2(54.0 / 64.0, 20.0 / 64.0));
104 | vec4 samp2 = texture(Sampler0, vec2(55.0 / 64.0, 20.0 / 64.0));
105 | bool slim = samp1.a == 0.0 || (((samp1.r + samp1.g + samp1.b) == 0.0) && ((samp2.r + samp2.g + samp2.b) == 0.0) && samp1.a == 1.0 && samp2.a == 1.0);
106 | int outerLayer = (gl_VertexID / 24) % 2;
107 | int vertexId = gl_VertexID % 4;
108 | int faceId = (gl_VertexID % 24) / 4;
109 | ivec2 faceIdTmp = ivec2(round(UV0 * SKINRES));
110 | if ((faceId != 1 && vertexId >= 2) || (faceId == 1 && vertexId <= 1)) {
111 | faceIdTmp.y -= FACERES;
112 | }
113 | if (vertexId == 0 || vertexId == 3) {
114 | faceIdTmp.x -= FACERES;
115 | }
116 | faceIdTmp /= FACERES;
117 | faceId = (faceIdTmp.x % 4) + 4 * faceIdTmp.y;
118 | faceId = faceremap[faceId];
119 | int subuvIndex = faceId;
120 |
121 | wpos.y += SPACING * partId;
122 | gl_Position = ProjMat * ModelViewMat * vec4(inverse(IViewRotMat) * wpos, 1.0);
123 |
124 | UVout = origins[2 * (partId - 1) + outerLayer];
125 | UVout2 = origins[2 * (partId - 1)];
126 |
127 | if (slim && (partId == 1 || partId == 2)) {
128 | subuvIndex += 6;
129 | }
130 | else if (partId == 3) {
131 | subuvIndex += 12;
132 | }
133 |
134 | vec4 subuv = subuvs[subuvIndex];
135 |
136 | vec2 offset = vec2(0.0);
137 | if (faceId == 1) {
138 | if (vertexId == 0) {
139 | offset += subuv.zw;
140 | }
141 | else if (vertexId == 1) {
142 | offset += subuv.xw;
143 | }
144 | else if (vertexId == 2) {
145 | offset += subuv.xy;
146 | }
147 | else {
148 | offset += subuv.zy;
149 | }
150 | }
151 | else {
152 | if (vertexId == 0) {
153 | offset += subuv.zy;
154 | }
155 | else if (vertexId == 1) {
156 | offset += subuv.xy;
157 | }
158 | else if (vertexId == 2) {
159 | offset += subuv.xw;
160 | }
161 | else {
162 | offset += subuv.zw;
163 | }
164 | }
165 |
166 | UVout += offset;
167 | UVout2 += offset;
168 | UVout /= float(SKINRES);
169 | UVout2 /= float(SKINRES);
170 | }
171 |
172 | vertexDistance = fog_distance(wpos, FogShape);
173 | texCoord0 = UVout;
174 | texCoord1 = UVout2;
175 | }
176 | }
--------------------------------------------------------------------------------
/resourcepack/pack.mcmeta:
--------------------------------------------------------------------------------
1 | {"pack":{"pack_format":46,"description":"Stable Player Display RP"}}
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | rootProject.name = "BlockPhysics"
2 |
3 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/Main.java:
--------------------------------------------------------------------------------
1 | package dev.emortal;
2 |
3 | import com.jme3.bullet.PhysicsSpace;
4 | import com.jme3.bullet.collision.PhysicsRayTestResult;
5 | import com.jme3.bullet.collision.shapes.BoxCollisionShape;
6 | import com.jme3.bullet.collision.shapes.CollisionShape;
7 | import com.jme3.bullet.objects.PhysicsBody;
8 | import com.jme3.bullet.objects.PhysicsRigidBody;
9 | import com.jme3.math.Vector3f;
10 | import dev.emortal.commands.*;
11 | import dev.emortal.objects.BlockRigidBody;
12 | import dev.emortal.objects.ChainPhysics;
13 | import dev.emortal.objects.LanternPhysics;
14 | import dev.emortal.objects.MinecraftPhysicsObject;
15 | import dev.emortal.tools.*;
16 | import electrostatic4j.snaploader.LibraryInfo;
17 | import electrostatic4j.snaploader.LoadingCriterion;
18 | import electrostatic4j.snaploader.NativeBinaryLoader;
19 | import electrostatic4j.snaploader.filesystem.DirectoryPath;
20 | import electrostatic4j.snaploader.platform.NativeDynamicLibrary;
21 | import electrostatic4j.snaploader.platform.util.PlatformPredicate;
22 | import net.kyori.adventure.bossbar.BossBar;
23 | import net.kyori.adventure.text.Component;
24 | import net.minestom.server.MinecraftServer;
25 | import net.minestom.server.ServerFlag;
26 | import net.minestom.server.command.CommandManager;
27 | import net.minestom.server.coordinate.Point;
28 | import net.minestom.server.coordinate.Pos;
29 | import net.minestom.server.coordinate.Vec;
30 | import net.minestom.server.entity.Entity;
31 | import net.minestom.server.entity.EntityType;
32 | import net.minestom.server.entity.GameMode;
33 | import net.minestom.server.event.GlobalEventHandler;
34 | import net.minestom.server.event.item.ItemDropEvent;
35 | import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
36 | import net.minestom.server.event.player.PlayerBlockBreakEvent;
37 | import net.minestom.server.event.player.PlayerBlockPlaceEvent;
38 | import net.minestom.server.event.player.PlayerSpawnEvent;
39 | import net.minestom.server.event.server.ServerTickMonitorEvent;
40 | import net.minestom.server.extras.MojangAuth;
41 | import net.minestom.server.instance.InstanceContainer;
42 | import net.minestom.server.instance.batch.AbsoluteBlockBatch;
43 | import net.minestom.server.instance.block.Block;
44 | import net.minestom.server.item.ItemStack;
45 | import net.minestom.server.item.Material;
46 | import net.minestom.server.network.packet.server.play.ExplosionPacket;
47 | import net.minestom.server.particle.Particle;
48 | import net.minestom.server.sound.SoundEvent;
49 | import net.minestom.server.timer.TaskSchedule;
50 | import net.minestom.server.utils.NamespaceID;
51 | import net.minestom.server.world.DimensionType;
52 | import org.jetbrains.annotations.NotNull;
53 | import org.slf4j.Logger;
54 | import org.slf4j.LoggerFactory;
55 |
56 | import java.text.DecimalFormat;
57 | import java.util.List;
58 | import java.util.Set;
59 | import java.util.concurrent.ThreadLocalRandom;
60 |
61 | import static dev.emortal.utils.CoordinateUtils.toVector3;
62 |
63 | public class Main {
64 |
65 | private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
66 |
67 | private static final Set BLOCKS_IN_SPHERE = SphereUtil.getBlocksInSphere(5);
68 |
69 |
70 |
71 | public static boolean paused = false;
72 |
73 | public static void main(String[] args) throws Exception {
74 | LibraryInfo info = new LibraryInfo(
75 | new DirectoryPath("linux/x86-64/com/github/stephengold"),
76 | "bulletjme", DirectoryPath.USER_DIR);
77 | NativeBinaryLoader loader = new NativeBinaryLoader(info);
78 | NativeDynamicLibrary[] libraries = new NativeDynamicLibrary[]{
79 | // new NativeDynamicLibrary("native/linux/arm64", PlatformPredicate.LINUX_ARM_64),
80 | // new NativeDynamicLibrary("native/linux/arm32", PlatformPredicate.LINUX_ARM_32),
81 | new NativeDynamicLibrary("native/linux/x86_64", PlatformPredicate.LINUX_X86_64),
82 | // new NativeDynamicLibrary("native/osx/arm64", PlatformPredicate.MACOS_ARM_64),
83 | // new NativeDynamicLibrary("native/osx/x86_64", PlatformPredicate.MACOS_X86_64),
84 | // new NativeDynamicLibrary("native/windows/x86_64", PlatformPredicate.WIN_X86_64)
85 | };
86 | loader.registerNativeLibraries(libraries).initPlatformLibrary();
87 | loader.loadLibrary(LoadingCriterion.INCREMENTAL_LOADING);
88 |
89 | System.setProperty("minestom.tps", "60");
90 |
91 | MinecraftServer server = MinecraftServer.init();
92 | MojangAuth.init();
93 |
94 |
95 | DimensionType fullbrightDimension = DimensionType.builder().ambientLight(1f).build();
96 | var fullbright = MinecraftServer.getDimensionTypeRegistry().register(NamespaceID.from("fullbright"), fullbrightDimension);
97 |
98 | InstanceContainer instance = MinecraftServer.getInstanceManager().createInstanceContainer(fullbright);
99 |
100 | for (int x = -20; x < 20; x++) {
101 | for (int z = -20; z < 20; z++) {
102 | instance.setBlock(x, -1, z, Block.GRASS_BLOCK);
103 | }
104 | }
105 | instance.setTimeSynchronizationTicks(0);
106 | instance.setTimeRate(0);
107 |
108 | MinecraftPhysics physicsHandler = new MinecraftPhysics(instance);
109 |
110 | BossBar bossBar = BossBar.bossBar(Component.empty(), 1f, BossBar.Color.GREEN, BossBar.Overlay.PROGRESS);
111 |
112 | GlobalEventHandler global = MinecraftServer.getGlobalEventHandler();
113 |
114 | global.addListener(PlayerSpawnEvent.class, e -> {
115 | e.getPlayer().showBossBar(bossBar);
116 | e.getPlayer().setGameMode(GameMode.CREATIVE);
117 |
118 | e.getPlayer().sendMessage(Component.text("Welcome to your physics playground!"));
119 | e.getPlayer().sendMessage(Component.text("Check your inventory for more tools"));
120 | e.getPlayer().sendMessage(Component.text("Use /clear to clear all objects in the world"));
121 |
122 | e.getPlayer().getInventory().setItemStack(9, new DiamondLayerTool(e.getPlayer(), physicsHandler).getItem());
123 | e.getPlayer().getInventory().setItemStack(7, new DeleteTool(e.getPlayer(), physicsHandler).getItem());
124 | e.getPlayer().getInventory().setItemStack(6, new PlayerSpawnerTool(e.getPlayer(), physicsHandler).getItem());
125 | e.getPlayer().getInventory().setItemStack(5, new GrabberTool(e.getPlayer(), physicsHandler).getItem());
126 | e.getPlayer().getInventory().setItemStack(4, new WeldTool(e.getPlayer(), physicsHandler).getItem());
127 |
128 | e.getPlayer().getInventory().setItemStack(3, ItemStack.of(Material.CHAIN));
129 | e.getPlayer().getInventory().setItemStack(2, ItemStack.of(Material.TNT));
130 | e.getPlayer().getInventory().setItemStack(1, ItemStack.of(Material.STONE));
131 |
132 | CollisionShape boxShape = new BoxCollisionShape((float) (e.getPlayer().getBoundingBox().width()/2f), (float) (e.getPlayer().getBoundingBox().height()/2f), (float) (e.getPlayer().getBoundingBox().depth()/2f));
133 | PhysicsRigidBody playerRigidBody = new PhysicsRigidBody(boxShape, PhysicsRigidBody.massForStatic);
134 | physicsHandler.getPhysicsSpace().addCollisionObject(playerRigidBody);
135 |
136 | e.getPlayer().setTag(MinecraftPhysics.PLAYER_RIGID_BODY_TAG, playerRigidBody);
137 |
138 | e.getPlayer().scheduler().buildTask(() -> {
139 | playerRigidBody.activate();
140 | playerRigidBody.setPhysicsLocation(toVector3(e.getPlayer().getPosition().add(0, 1, 0)));
141 | }).repeat(TaskSchedule.tick(1)).schedule();
142 | });
143 |
144 | global.addListener(AsyncPlayerConfigurationEvent.class, e -> {
145 | e.setSpawningInstance(instance);
146 | e.getPlayer().setRespawnPoint(new Pos(0, 20, 0));
147 | });
148 |
149 | instance.scheduler().buildTask(new Runnable() {
150 | long lastRan = System.nanoTime();
151 | @Override
152 | public void run() {
153 | long diff = System.nanoTime() - lastRan;
154 | float deltaTime = diff / 1_000_000_000f;
155 |
156 | lastRan = System.nanoTime();
157 | if (paused) return;
158 | physicsHandler.update(deltaTime);
159 | }
160 | }).repeat(TaskSchedule.tick(1)).schedule();
161 |
162 | global.addListener(ItemDropEvent.class, e -> {
163 | e.setCancelled(true);
164 | });
165 |
166 | DecimalFormat dec = new DecimalFormat("0.00");
167 | global.addListener(ServerTickMonitorEvent.class, e -> {
168 | double tickTime = Math.floor(e.getTickMonitor().getTickTime() * 100.0) / 100.0;
169 | bossBar.name(
170 | Component.text()
171 | .append(Component.text("MSPT: " + dec.format(tickTime)))
172 | );
173 | bossBar.progress(Math.min((float)tickTime / (float)(1000 / ServerFlag.SERVER_TICKS_PER_SECOND), 1f));
174 |
175 | if (tickTime > MinecraftServer.TICK_MS) {
176 | bossBar.color(BossBar.Color.RED);
177 | } else {
178 | bossBar.color(BossBar.Color.GREEN);
179 | }
180 | });
181 |
182 | // TODO: kill barrier / floor
183 |
184 | global.addListener(PlayerBlockPlaceEvent.class, e -> {
185 | Point blockPos = e.getBlockPosition();
186 |
187 | if (e.getBlock().compare(Block.PLAYER_HEAD) || e.getBlock().compare(Block.PLAYER_WALL_HEAD)) {
188 | e.setCancelled(true);
189 | }
190 |
191 | // if (e.getBlock().compare(Block.TRAPDOOR)) {
192 | // e.setCancelled(true);
193 | //
194 | // new TrapdoorPhysics(physicsHandler, null, instance, new Vector3f(0.5f, 0.5f, 0.1f), 1, new Vector3f(e.getBlockPosition().blockX() + 0.5f, e.getBlockPosition().blockY() + 1.5f, e.getBlockPosition().blockZ() + 0.5f));
195 | // }
196 |
197 | if (e.getBlock().compare(Block.LANTERN)) {
198 | e.setCancelled(true);
199 |
200 | LanternPhysics lantern = new LanternPhysics(physicsHandler, null, new Vec(0.1f, 0.4f, 0.1f), 1, new Vector3f(e.getBlockPosition().blockX() + 0.5f, e.getBlockPosition().blockY() + 1.5f, e.getBlockPosition().blockZ() + 0.5f));
201 | lantern.setInstance();
202 | }
203 |
204 | if (e.getBlock().compare(Block.CHAIN)) {
205 | e.setCancelled(true);
206 |
207 | MinecraftPhysicsObject first = new ChainPhysics(physicsHandler, null, new Vec(0.1f, 0.5f, 0.1f), 1, new Vector3f(e.getBlockPosition().blockX() + 0.5f, e.getBlockPosition().blockY() + 0.5f, e.getBlockPosition().blockZ() + 0.5f));
208 | MinecraftPhysicsObject lastLink = first;
209 | for (int links = 0; links < ChainLengthCommand.CHAIN_LENGTH; links++) {
210 | lastLink = new ChainPhysics(physicsHandler, (PhysicsRigidBody) lastLink.getCollisionObject(), new Vec(0.1f, 0.5f, 0.1f), 1, new Vector3f(e.getBlockPosition().blockX() + 0.5f, e.getBlockPosition().blockY() + 0.5f + (ChainLengthCommand.CHAIN_LENGTH-links) * 1.1f, e.getBlockPosition().blockZ() + 0.5f));
211 | lastLink.setInstance();
212 | }
213 | // Main.paused = true;
214 | }
215 |
216 | if (e.getBlock().compare(Block.TNT)) {
217 | e.setCancelled(true);
218 |
219 | Entity entity = new Entity(EntityType.TNT);
220 | // entity.editEntityMeta(PrimedTntMeta.class, meta -> {
221 | // meta.setFuseTime(60);
222 | // });
223 | // TODO: Causes network protocol error - minestom bug
224 | entity.setInstance(instance, e.getBlockPosition().add(0.5, 0, 0.5));
225 |
226 | instance.scheduler().buildTask(() -> {
227 | ExplosionPacket packet = new ExplosionPacket(entity.getPosition(), Vec.ZERO, Particle.EXPLOSION_EMITTER, SoundEvent.ENTITY_GENERIC_EXPLODE);
228 | instance.sendGroupedPacket(packet);
229 | entity.remove();
230 |
231 | ThreadLocalRandom rand = ThreadLocalRandom.current();
232 |
233 | for (MinecraftPhysicsObject cube : physicsHandler.getObjects()) {
234 | if (cube.getEntity() == null) continue;
235 | if (cube.getEntity().getPosition().distanceSquared(blockPos.add(0.5)) > 5*5) continue;
236 |
237 | PhysicsRigidBody cubeRigidBody = (PhysicsRigidBody) cube.getCollisionObject();
238 |
239 | Vec velocity = Vec.fromPoint(cube.getEntity().getPosition().sub(blockPos.add(0.5))).normalize().mul(4, 8, 4).mul(rand.nextDouble(1.2, 2)).mul(TntStrengthCommand.TNT_STRENGTH);
240 | cube.getCollisionObject().activate(true);
241 |
242 | Vector3f linearVelocity = new Vector3f();
243 | cubeRigidBody.getLinearVelocity(linearVelocity);
244 | cubeRigidBody.setLinearVelocity(linearVelocity.add(toVector3(velocity)));
245 | cubeRigidBody.setAngularVelocity(toVector3(velocity)); // probably completely wrong but it looks nice
246 | }
247 |
248 | List nearbyBlocks = SphereUtil.getNearbyBlocks(blockPos.add(0.5), BLOCKS_IN_SPHERE, instance, block -> !block.block().isAir() && !block.block().compare(Block.GRASS_BLOCK));
249 | AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
250 | for (WorldBlock nearbyBlock : nearbyBlocks) {
251 | var cube = new BlockRigidBody(physicsHandler, toVector3(nearbyBlock.position()), new Vec(0.5), 1, true, nearbyBlock.block());
252 | cube.setInstance();
253 | PhysicsRigidBody cubeRigidBody = (PhysicsRigidBody) cube.getCollisionObject();
254 |
255 | Vec velocity = Vec.fromPoint(nearbyBlock.position().sub(blockPos.add(0.5))).normalize().mul(4, 8, 4).mul(rand.nextDouble(1.2, 2)).mul(TntStrengthCommand.TNT_STRENGTH);
256 | cube.getCollisionObject().activate(true);
257 | Vector3f linearVelocity = new Vector3f();
258 | cubeRigidBody.getLinearVelocity(linearVelocity);
259 | cubeRigidBody.setLinearVelocity(linearVelocity.add(toVector3(velocity)));
260 | cubeRigidBody.setAngularVelocity(toVector3(velocity)); // probably completely wrong but it looks nice
261 | batch.setBlock(nearbyBlock.position(), Block.AIR);
262 |
263 | Block block = instance.getBlock(nearbyBlock.position().sub(0.5));
264 | MinecraftPhysicsObject physicsBlock = block.getTag(MinecraftPhysics.PHYSICS_BLOCK_TAG);
265 | if (physicsBlock != null) {
266 | physicsBlock.destroy();
267 | }
268 | }
269 | batch.apply(instance, null);
270 | }).delay(TaskSchedule.tick(3 * ServerFlag.SERVER_TICKS_PER_SECOND)).schedule();
271 | }
272 |
273 |
274 |
275 | if (!e.isCancelled()) {
276 | MinecraftPhysicsObject object = new BlockRigidBody(physicsHandler, toVector3(blockPos.add(0.5)), new Vec(0.5), PhysicsBody.massForStatic, false, Block.BLUE_STAINED_GLASS);
277 | object.setInstance();
278 |
279 | e.setBlock(e.getBlock().withTag(MinecraftPhysics.PHYSICS_BLOCK_TAG, object));
280 | }
281 | });
282 |
283 | global.addListener(PlayerBlockBreakEvent.class, e -> {
284 | MinecraftPhysicsObject physicsObject = e.getBlock().getTag(MinecraftPhysics.PHYSICS_BLOCK_TAG);
285 | if (physicsObject != null) {
286 | physicsObject.destroy();
287 | }
288 | });
289 |
290 | CommandManager commandManager = MinecraftServer.getCommandManager();
291 | commandManager.register(new ChainLengthCommand());
292 | commandManager.register(new ClearCommand(physicsHandler));
293 | commandManager.register(new PerformanceCommand(MinecraftServer.getGlobalEventHandler(), physicsHandler));
294 | commandManager.register(new PlayerSizeCommand());
295 | commandManager.register(new TntStrengthCommand());
296 |
297 | server.start("0.0.0.0", 25565);
298 | }
299 |
300 | public static List raycastEntity(PhysicsSpace physicsSpace, @NotNull Point startPoint, @NotNull Point direction, double maxDistance) {
301 | Point endPoint = startPoint.add(Vec.fromPoint(direction).normalize().mul(maxDistance));
302 | List results = physicsSpace.rayTest(new Vector3f((float) startPoint.x(), (float) startPoint.y(), (float) startPoint.z()), new Vector3f((float) endPoint.x(), (float) endPoint.y(), (float) endPoint.z()));
303 |
304 | return results;
305 | }
306 |
307 | }
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/MinecraftPhysics.java:
--------------------------------------------------------------------------------
1 | package dev.emortal;
2 |
3 | import com.jme3.bullet.NativePhysicsObject;
4 | import com.jme3.bullet.PhysicsSpace;
5 | import com.jme3.bullet.collision.shapes.CollisionShape;
6 | import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
7 | import com.jme3.bullet.objects.PhysicsRigidBody;
8 | import com.jme3.math.Plane;
9 | import com.jme3.math.Vector3f;
10 | import dev.emortal.objects.MinecraftPhysicsObject;
11 | import net.minestom.server.instance.Instance;
12 | import net.minestom.server.tag.Tag;
13 | import org.jetbrains.annotations.NotNull;
14 | import org.jetbrains.annotations.Nullable;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import java.util.List;
19 | import java.util.Map;
20 | import java.util.concurrent.ConcurrentHashMap;
21 | import java.util.concurrent.CopyOnWriteArrayList;
22 |
23 | public class MinecraftPhysics {
24 |
25 | private static final Logger LOGGER = LoggerFactory.getLogger(MinecraftPhysics.class);
26 |
27 | private @NotNull PhysicsSpace physicsSpace;
28 | private @NotNull PhysicsRigidBody floor;
29 |
30 | public static final Tag PHYSICS_BLOCK_TAG = Tag.Transient("physicsblock");
31 | public static final Tag PLAYER_RIGID_BODY_TAG = Tag.Transient("playerrigidbody");
32 |
33 |
34 | private final @NotNull List objects = new CopyOnWriteArrayList<>();
35 | private final @NotNull Map objectMap = new ConcurrentHashMap<>();
36 | private final Instance instance;
37 |
38 | public MinecraftPhysics(Instance instance) {
39 | this.instance = instance;
40 |
41 | instance.scheduleNextTick((a) -> {
42 | physicsSpace = new PhysicsSpace(PhysicsSpace.BroadphaseType.DBVT);
43 |
44 | // Default: -9.81f
45 | // Minecraft: -31.36f
46 | physicsSpace.setGravity(new Vector3f(0, -17f, 0));
47 |
48 | CollisionShape planeShape = new PlaneCollisionShape(new Plane(Vector3f.UNIT_Y, 0f));
49 | floor = new PhysicsRigidBody(planeShape, PhysicsRigidBody.massForStatic);
50 |
51 | physicsSpace.add(floor);
52 | });
53 | }
54 |
55 | public void update(float delta) {
56 | if (physicsSpace == null) return;
57 |
58 | physicsSpace.update(delta);
59 |
60 | for (MinecraftPhysicsObject object : objects) {
61 | // object.getRigidBody().isInWorld()
62 | // TODO: use this to automatically remove?
63 | object.update();
64 | }
65 | }
66 |
67 | public @NotNull List getObjects() {
68 | return objects;
69 | }
70 |
71 | public void addObject(MinecraftPhysicsObject object) {
72 | objects.add(object);
73 | objectMap.put(object.getCollisionObject(), object);
74 | }
75 | public void removeObject(MinecraftPhysicsObject object) {
76 | objects.remove(object);
77 | objectMap.remove(object.getCollisionObject());
78 | }
79 |
80 | public @Nullable MinecraftPhysicsObject getObjectByPhysicsObject(NativePhysicsObject physicsObject) {
81 | return objectMap.get(physicsObject);
82 | }
83 |
84 | public Instance getInstance() {
85 | return instance;
86 | }
87 |
88 | public PhysicsSpace getPhysicsSpace() {
89 | return physicsSpace;
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/NoTickingEntity.java:
--------------------------------------------------------------------------------
1 | package dev.emortal;
2 |
3 | import net.minestom.server.entity.Entity;
4 | import net.minestom.server.entity.EntityType;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | public class NoTickingEntity extends Entity {
8 | public NoTickingEntity(@NotNull EntityType entityType) {
9 | super(entityType);
10 |
11 | this.hasPhysics = false;
12 | setNoGravity(true);
13 | }
14 |
15 | @Override
16 | public void tick(long time) {
17 |
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/PlayerDisplayPart.java:
--------------------------------------------------------------------------------
1 | package dev.emortal;
2 |
3 | public enum PlayerDisplayPart {
4 | HEAD(0, "animated_java:blueprint/player_display/head"),
5 | RIGHT_ARM(-1024, "animated_java:blueprint/player_display/right_arm"),
6 | LEFT_ARM(-2048, "animated_java:blueprint/player_display/left_arm"),
7 | TORSO(-3072, "animated_java:blueprint/player_display/torso"),
8 | RIGHT_LEG(-4096, "animated_java:blueprint/player_display/right_leg"),
9 | LEFT_LEG(-5120, "animated_java:blueprint/player_display/left_leg");
10 |
11 | private final double yTranslation;
12 | private final String customModelData;
13 | PlayerDisplayPart(double yTranslation, String customModelData) {
14 | this.yTranslation = yTranslation;
15 | this.customModelData = customModelData;
16 | }
17 |
18 | public double getYTranslation() {
19 | return yTranslation;
20 | }
21 |
22 | public String getCustomModelData() {
23 | return customModelData;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/SphereUtil.java:
--------------------------------------------------------------------------------
1 | package dev.emortal;
2 |
3 | import net.minestom.server.coordinate.Point;
4 | import net.minestom.server.coordinate.Vec;
5 | import net.minestom.server.instance.Instance;
6 | import net.minestom.server.instance.block.Block;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | import java.util.*;
10 | import java.util.function.Predicate;
11 |
12 | public final class SphereUtil {
13 |
14 | public static @NotNull List getNearbyBlocks(@NotNull Point pos, Set blocksInSphere, @NotNull Instance instance,
15 | @NotNull Predicate predicate) {
16 | List filteredBlocks = new ArrayList<>();
17 | for (Point block : blocksInSphere) {
18 | Point blockPos = block.add(pos);
19 | Block currentBlock;
20 | try {
21 | currentBlock = instance.getBlock(blockPos, Block.Getter.Condition.TYPE);
22 | } catch (Exception ignored) {
23 | continue;
24 | }
25 | if (!predicate.test(new WorldBlock(blockPos, currentBlock))) continue;
26 |
27 | filteredBlocks.add(new WorldBlock(blockPos, currentBlock));
28 | }
29 |
30 | Collections.shuffle(filteredBlocks);
31 | return filteredBlocks;
32 | }
33 |
34 | public static @NotNull Set getBlocksInSphere(double radius) {
35 | Set points = new HashSet<>();
36 |
37 | for (double x = -radius; x <= radius; x++) {
38 | for (double y = -radius; y <= radius; y++) {
39 | for (double z = -radius; z <= radius; z++) {
40 | if ((x * x) + (y * y) + (z * z) > radius * radius) continue;
41 | points.add(new Vec(x, y, z));
42 | }
43 | }
44 | }
45 |
46 | return points;
47 | }
48 |
49 | private SphereUtil() {
50 | }
51 | }
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/WorldBlock.java:
--------------------------------------------------------------------------------
1 | package dev.emortal;
2 |
3 | import net.minestom.server.coordinate.Point;
4 | import net.minestom.server.instance.block.Block;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | public record WorldBlock(@NotNull Point position, @NotNull Block block) {
8 | }
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/commands/ChainLengthCommand.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.commands;
2 |
3 | import net.kyori.adventure.text.Component;
4 | import net.minestom.server.command.builder.Command;
5 | import net.minestom.server.command.builder.arguments.ArgumentType;
6 |
7 | public class ChainLengthCommand extends Command {
8 | public static int CHAIN_LENGTH = 10;
9 |
10 | public ChainLengthCommand() {
11 | super("chainlength");
12 |
13 | var intArg = ArgumentType.Integer("chainLength").between(1, 100);
14 | addSyntax((sender, ctx) -> {
15 | CHAIN_LENGTH = ctx.get(intArg);
16 |
17 | sender.sendMessage(Component.text("Set chain length to: " + CHAIN_LENGTH));
18 | }, intArg);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/commands/ClearCommand.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.commands;
2 |
3 | import com.jme3.bullet.collision.shapes.BoxCollisionShape;
4 | import com.jme3.bullet.collision.shapes.CollisionShape;
5 | import com.jme3.bullet.collision.shapes.PlaneCollisionShape;
6 | import com.jme3.bullet.joints.PhysicsJoint;
7 | import com.jme3.bullet.objects.PhysicsRigidBody;
8 | import com.jme3.math.Plane;
9 | import com.jme3.math.Vector3f;
10 | import dev.emortal.MinecraftPhysics;
11 | import net.minestom.server.command.builder.Command;
12 | import net.minestom.server.entity.Entity;
13 | import net.minestom.server.entity.Player;
14 | import net.minestom.server.timer.TaskSchedule;
15 |
16 | import static dev.emortal.utils.CoordinateUtils.toVector3;
17 |
18 | public class ClearCommand extends Command {
19 | public ClearCommand(MinecraftPhysics physicsHandler) {
20 | super("clear");
21 |
22 | setDefaultExecutor((sender, ctx) -> {
23 | if (!(sender instanceof Player player)) return;
24 |
25 | for (PhysicsRigidBody rigidBody : physicsHandler.getPhysicsSpace().getRigidBodyList()) {
26 | physicsHandler.getPhysicsSpace().removeCollisionObject(rigidBody);
27 | }
28 |
29 | for (PhysicsJoint physicsJoint : physicsHandler.getPhysicsSpace().getJointList()) {
30 | physicsJoint.getPhysicsSpace().removeJoint(physicsJoint);
31 | physicsJoint.destroy();
32 | }
33 |
34 | for (Entity entity : player.getInstance().getEntities()) {
35 | if (entity instanceof Player) continue;
36 | entity.remove();
37 | }
38 |
39 | physicsHandler.getObjects().clear();
40 |
41 | // Re-add the floor
42 | CollisionShape planeShape = new PlaneCollisionShape(new Plane(Vector3f.UNIT_Y, 0f));
43 | PhysicsRigidBody floor = new PhysicsRigidBody(planeShape, PhysicsRigidBody.massForStatic);
44 | physicsHandler.getPhysicsSpace().addCollisionObject(floor);
45 |
46 | for (Player player1 : player.getInstance().getPlayers()) {
47 | CollisionShape boxShape = new BoxCollisionShape((float) (player1.getBoundingBox().width()/2f), (float) (player1.getBoundingBox().height()/2f), (float) (player1.getBoundingBox().depth()/2f));
48 | PhysicsRigidBody playerRigidBody = new PhysicsRigidBody(boxShape, PhysicsRigidBody.massForStatic);
49 | physicsHandler.getPhysicsSpace().addCollisionObject(playerRigidBody);
50 |
51 | player1.setTag(MinecraftPhysics.PLAYER_RIGID_BODY_TAG, playerRigidBody);
52 |
53 | player1.scheduler().buildTask(() -> {
54 | playerRigidBody.activate();
55 | playerRigidBody.setPhysicsLocation(toVector3(player1.getPosition().add(0, 1, 0)));
56 | }).repeat(TaskSchedule.tick(1)).schedule();
57 | }
58 |
59 | System.gc();
60 |
61 | // sender.sendMessage("Cleared!");
62 | });
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/commands/PerformanceCommand.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.commands;
2 |
3 | import com.jme3.bullet.objects.PhysicsRigidBody;
4 | import dev.emortal.MinecraftPhysics;
5 | import net.kyori.adventure.text.Component;
6 | import net.kyori.adventure.text.format.NamedTextColor;
7 | import net.minestom.server.command.CommandSender;
8 | import net.minestom.server.command.builder.Command;
9 | import net.minestom.server.command.builder.CommandContext;
10 | import net.minestom.server.event.Event;
11 | import net.minestom.server.event.EventNode;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | public final class PerformanceCommand extends Command {
15 |
16 | private final MinecraftPhysics physicsHandler;
17 | public PerformanceCommand(@NotNull EventNode eventNode, MinecraftPhysics physicsHandler) {
18 | super("performance", "stats");
19 | this.physicsHandler = physicsHandler;
20 | this.addSyntax(this::onExecute);
21 | }
22 |
23 | private void onExecute(@NotNull CommandSender sender, @NotNull CommandContext context) {
24 | long totalMem = Runtime.getRuntime().totalMemory() / 1024 / 1024;
25 | long freeMem = Runtime.getRuntime().freeMemory() / 1024 / 1024;
26 | long ramUsage = totalMem - freeMem;
27 |
28 | int awakeRigid = 0;
29 | for (PhysicsRigidBody rigidBody : physicsHandler.getPhysicsSpace().getRigidBodyList()) {
30 | if (rigidBody.isActive()) awakeRigid++;
31 | }
32 |
33 | sender.sendMessage(Component.text()
34 | .append(Component.newline())
35 |
36 | // RAM usage information
37 | .append(Component.text("RAM Usage: ", NamedTextColor.GRAY))
38 | .append(Component.text(String.format("%sMB / %sMB", ramUsage, totalMem), NamedTextColor.GRAY))
39 | .append(Component.newline())
40 |
41 | // Physics Information
42 | .append(Component.text("Rigid Bodies: ", NamedTextColor.GRAY))
43 | .append(Component.text(physicsHandler.getPhysicsSpace().countRigidBodies(), NamedTextColor.GOLD))
44 | .append(Component.text(" (Awake: ", NamedTextColor.GRAY))
45 | .append(Component.text(awakeRigid, NamedTextColor.WHITE))
46 | .append(Component.text(")", NamedTextColor.GRAY))
47 | .append(Component.newline())
48 | .append(Component.text("Joints: ", NamedTextColor.GRAY))
49 | .append(Component.text(physicsHandler.getPhysicsSpace().countJoints(), NamedTextColor.GOLD)));
50 | }
51 | }
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/commands/PlayerSizeCommand.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.commands;
2 |
3 | import net.kyori.adventure.text.Component;
4 | import net.minestom.server.command.builder.Command;
5 | import net.minestom.server.command.builder.arguments.ArgumentType;
6 |
7 | public class PlayerSizeCommand extends Command {
8 | public static double PLAYER_SIZE = 1.0;
9 |
10 | public PlayerSizeCommand() {
11 | super("playersize");
12 |
13 | var doubleArg = ArgumentType.Double("playerSize").between(0.2, 10.0);
14 | addSyntax((sender, ctx) -> {
15 | PLAYER_SIZE = ctx.get(doubleArg);
16 |
17 | sender.sendMessage(Component.text("Set player size to: " + PLAYER_SIZE));
18 | }, doubleArg);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/commands/TntStrengthCommand.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.commands;
2 |
3 | import net.kyori.adventure.text.Component;
4 | import net.minestom.server.command.builder.Command;
5 | import net.minestom.server.command.builder.arguments.ArgumentType;
6 |
7 | public class TntStrengthCommand extends Command {
8 | public static double TNT_STRENGTH = 1.0;
9 |
10 | public TntStrengthCommand() {
11 | super("tntstrength");
12 |
13 | var doubleArg = ArgumentType.Double("tntStrength").between(0.2, 50.0);
14 | addSyntax((sender, ctx) -> {
15 | TNT_STRENGTH = ctx.get(doubleArg);
16 |
17 | sender.sendMessage(Component.text("Set tnt strength to: " + TNT_STRENGTH));
18 | }, doubleArg);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/objects/BlockRigidBody.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.objects;
2 |
3 | import com.jme3.bullet.collision.shapes.BoxCollisionShape;
4 | import com.jme3.bullet.objects.PhysicsRigidBody;
5 | import com.jme3.math.Vector3f;
6 | import dev.emortal.MinecraftPhysics;
7 | import dev.emortal.NoTickingEntity;
8 | import net.minestom.server.coordinate.Vec;
9 | import net.minestom.server.entity.Entity;
10 | import net.minestom.server.entity.EntityType;
11 | import net.minestom.server.entity.metadata.display.ItemDisplayMeta;
12 | import net.minestom.server.instance.block.Block;
13 | import net.minestom.server.item.ItemStack;
14 | import org.jetbrains.annotations.NotNull;
15 |
16 | public class BlockRigidBody extends MinecraftPhysicsObject {
17 |
18 | private final Block block;
19 | private final boolean visible;
20 |
21 | public BlockRigidBody(@NotNull MinecraftPhysics mcPhysics, Vector3f position, Vec size, float mass, boolean visible, Block block) {
22 | super(mcPhysics, new PhysicsRigidBody(new BoxCollisionShape((float)size.x(), (float)size.y(), (float)size.z()), mass), size);
23 |
24 | this.block = block;
25 | this.visible = visible;
26 |
27 | var rigidBody = (PhysicsRigidBody) getCollisionObject();
28 | rigidBody.setAngularDamping(0.1f);
29 | rigidBody.setLinearDamping(0.3f);
30 | rigidBody.setPhysicsLocation(position);
31 | }
32 |
33 | @Override
34 | public Entity createEntity() {
35 | if (!visible) return null;
36 |
37 | // Uses an ITEM_DISPLAY instead of a BLOCK_DISPLAY as it is centered around the middle instead of the corner
38 | // although causes issues with certain items, it works for most
39 | Entity entity = new NoTickingEntity(EntityType.ITEM_DISPLAY);
40 |
41 | entity.setBoundingBox(getSize().x(), getSize().y(), getSize().z());
42 |
43 | entity.editEntityMeta(ItemDisplayMeta.class, meta -> {
44 | meta.setWidth(2);
45 | meta.setHeight(2);
46 | meta.setItemStack(ItemStack.of(block.registry().material()));
47 | meta.setScale(getSize().mul(2));
48 | });
49 |
50 | return entity;
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/objects/ChainPhysics.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.objects;
2 |
3 | import com.jme3.bullet.RotationOrder;
4 | import com.jme3.bullet.collision.shapes.BoxCollisionShape;
5 | import com.jme3.bullet.joints.New6Dof;
6 | import com.jme3.bullet.objects.PhysicsRigidBody;
7 | import com.jme3.math.Matrix3f;
8 | import com.jme3.math.Vector3f;
9 | import dev.emortal.MinecraftPhysics;
10 | import dev.emortal.NoTickingEntity;
11 | import net.minestom.server.coordinate.Vec;
12 | import net.minestom.server.entity.Entity;
13 | import net.minestom.server.entity.EntityType;
14 | import net.minestom.server.entity.metadata.display.ItemDisplayMeta;
15 | import net.minestom.server.item.ItemStack;
16 | import net.minestom.server.item.Material;
17 | import org.jetbrains.annotations.Nullable;
18 |
19 | public class ChainPhysics extends MinecraftPhysicsObject {
20 |
21 | public ChainPhysics(MinecraftPhysics mcPhysics, @Nullable PhysicsRigidBody parent, Vec size, float mass, Vector3f jointPos) {
22 | super(mcPhysics, new PhysicsRigidBody(new BoxCollisionShape((float)size.x(), (float)size.y(), (float)size.z()), mass), size);
23 |
24 | var rigidBody = (PhysicsRigidBody) getCollisionObject();
25 | rigidBody.setAngularDamping(0.1f);
26 | rigidBody.setLinearDamping(0.3f);
27 | rigidBody.setPhysicsLocation(jointPos);
28 |
29 | if (parent == null) return;
30 |
31 | New6Dof joint = new New6Dof(parent, rigidBody, new Vector3f(0, -0.52f, 0), new Vector3f(0, 0.52f, 0), Matrix3f.IDENTITY, Matrix3f.IDENTITY, RotationOrder.XYZ);
32 | joint.setBreakingImpulseThreshold(70);
33 | joint.setCollisionBetweenLinkedBodies(false);
34 | mcPhysics.getPhysicsSpace().addJoint(joint);
35 | }
36 |
37 | @Override
38 | public @Nullable Entity createEntity() {
39 | // Uses an ITEM_DISPLAY instead of a BLOCK_DISPLAY as it is centered around the middle instead of the corner
40 | Entity entity = new NoTickingEntity(EntityType.ITEM_DISPLAY);
41 |
42 | // entity.setBoundingBox(size.x, size.y, size.z);
43 | entity.setBoundingBox(0.2, getSize().y(), 0.2);
44 |
45 | entity.editEntityMeta(ItemDisplayMeta.class, meta -> {
46 | meta.setWidth(2);
47 | meta.setHeight(2);
48 | meta.setItemStack(ItemStack.of(Material.CHAIN));
49 | // meta.setItemStack(ItemStack.of(Material.DIAMOND_BLOCK));
50 | // meta.setScale(toVec(transform.getScale()).mul(size.x * 2, size.y * 2, size.z * 2));
51 | meta.setScale(new Vec(1.15));
52 | // meta.setScale(getSize().mul(2));
53 | });
54 |
55 | return entity;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/objects/LanternPhysics.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.objects;
2 |
3 | import com.jme3.bullet.collision.shapes.BoxCollisionShape;
4 | import com.jme3.bullet.collision.shapes.CollisionShape;
5 | import com.jme3.bullet.joints.ConeJoint;
6 | import com.jme3.bullet.objects.PhysicsRigidBody;
7 | import com.jme3.math.Vector3f;
8 | import dev.emortal.MinecraftPhysics;
9 | import dev.emortal.NoTickingEntity;
10 | import net.minestom.server.coordinate.Vec;
11 | import net.minestom.server.entity.Entity;
12 | import net.minestom.server.entity.EntityType;
13 | import net.minestom.server.entity.metadata.display.ItemDisplayMeta;
14 | import net.minestom.server.item.ItemStack;
15 | import net.minestom.server.item.Material;
16 | import org.jetbrains.annotations.Nullable;
17 |
18 | public class LanternPhysics extends MinecraftPhysicsObject {
19 |
20 | public LanternPhysics(MinecraftPhysics mcPhysics, @Nullable PhysicsRigidBody parent, Vec size, float mass, Vector3f jointPos) {
21 | super(mcPhysics, new PhysicsRigidBody(new BoxCollisionShape((float)size.x(), (float)size.y(), (float)size.z()), mass), size);
22 |
23 | setAlwaysActive(true); // Lanterns may randomly stop otherwise
24 |
25 | var rigidBody = (PhysicsRigidBody) getCollisionObject();
26 | rigidBody.setAngularDamping(0.1f);
27 | rigidBody.setLinearDamping(0.3f);
28 | rigidBody.setPhysicsLocation(jointPos);
29 | rigidBody.setPhysicsLocation(jointPos.subtract(0, 1f, 0f));
30 |
31 | if (parent == null) {
32 | CollisionShape parentBoxShape = new BoxCollisionShape(0.1f);
33 | PhysicsRigidBody parentlessRigidBody = new PhysicsRigidBody(parentBoxShape, PhysicsRigidBody.massForStatic);
34 | parentlessRigidBody.setPhysicsLocation(jointPos);
35 | mcPhysics.getPhysicsSpace().addCollisionObject(parentlessRigidBody);
36 | parent = parentlessRigidBody;
37 | addRelated(parent);
38 | }
39 |
40 | ConeJoint joint = new ConeJoint(parent, rigidBody, new Vector3f(0, -0.5f, 0), new Vector3f(0, 0.5f, 0));
41 | addRelated(joint);
42 | mcPhysics.getPhysicsSpace().addJoint(joint);
43 | }
44 |
45 | @Override
46 | public Entity createEntity() {
47 | // Uses an ITEM_DISPLAY instead of a BLOCK_DISPLAY as it is centered around the middle instead of the corner
48 | Entity entity = new NoTickingEntity(EntityType.ITEM_DISPLAY);
49 |
50 | entity.setBoundingBox(0.2, getSize().y(), 0.2);
51 |
52 | entity.editEntityMeta(ItemDisplayMeta.class, meta -> {
53 | meta.setWidth(2);
54 | meta.setHeight(2);
55 | meta.setTranslation(new Vec(0, 0.3, 0));
56 | meta.setItemStack(ItemStack.of(Material.LANTERN));
57 | });
58 |
59 | return entity;
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/objects/MinecraftPhysicsObject.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.objects;
2 |
3 | import com.jme3.bullet.NativePhysicsObject;
4 | import com.jme3.bullet.collision.PhysicsCollisionObject;
5 | import com.jme3.bullet.joints.PhysicsJoint;
6 | import com.jme3.bullet.objects.PhysicsRigidBody;
7 | import com.jme3.math.Transform;
8 | import dev.emortal.MinecraftPhysics;
9 | import net.minestom.server.coordinate.Vec;
10 | import net.minestom.server.entity.Entity;
11 | import net.minestom.server.entity.metadata.display.AbstractDisplayMeta;
12 | import org.jetbrains.annotations.NotNull;
13 | import org.jetbrains.annotations.Nullable;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | import static dev.emortal.utils.CoordinateUtils.*;
19 |
20 | public abstract class MinecraftPhysicsObject {
21 |
22 | private final List relatedObjects = new ArrayList<>();
23 |
24 | private final @NotNull MinecraftPhysics mcPhysics;
25 | private final @NotNull PhysicsCollisionObject collisionObject;
26 | private @NotNull Vec size;
27 | private @Nullable Entity entity;
28 | private boolean alwaysActive = false;
29 | public MinecraftPhysicsObject(@NotNull MinecraftPhysics mcPhysics, @NotNull PhysicsCollisionObject collisionObject, @NotNull Vec size) {
30 | this.mcPhysics = mcPhysics;
31 | this.collisionObject = collisionObject;
32 | this.size = size;
33 |
34 | mcPhysics.getPhysicsSpace().add(collisionObject);
35 | mcPhysics.addObject(this);
36 |
37 |
38 | }
39 |
40 | public Entity setInstance() {
41 | this.entity = createEntity();
42 | if (this.entity != null) {
43 | Transform transform = new Transform();
44 | collisionObject.getTransform(transform);
45 | this.entity.setInstance(mcPhysics.getInstance(), toVec(transform.getTranslation()));
46 | }
47 | return this.entity;
48 | }
49 |
50 | public void addRelated(NativePhysicsObject related) {
51 | this.relatedObjects.add(related);
52 | }
53 |
54 | public void destroy() {
55 | if (collisionObject instanceof PhysicsRigidBody rigidBody) {
56 | for (PhysicsJoint physicsJoint : rigidBody.listJoints()) {
57 | mcPhysics.getPhysicsSpace().remove(physicsJoint);
58 | }
59 | }
60 |
61 | for (NativePhysicsObject relatedObject : relatedObjects) {
62 | mcPhysics.getPhysicsSpace().remove(relatedObject);
63 | }
64 | mcPhysics.getPhysicsSpace().remove(collisionObject);
65 | mcPhysics.removeObject(this);
66 | if (entity != null) {
67 | entity.remove();
68 | }
69 | }
70 |
71 | public @NotNull PhysicsCollisionObject getCollisionObject() {
72 | return collisionObject;
73 | }
74 |
75 | public void setAlwaysActive(boolean alwaysActive) {
76 | this.alwaysActive = alwaysActive;
77 | }
78 |
79 | public abstract @Nullable Entity createEntity();
80 |
81 | public @Nullable Entity getEntity() {
82 | return entity;
83 | }
84 |
85 | public @NotNull Vec getSize() {
86 | return size;
87 | }
88 |
89 | public void update() {
90 | if (entity == null) return;
91 | if (!entity.isActive()) return;
92 | if (alwaysActive) collisionObject.activate(true);
93 |
94 | entity.editEntityMeta(AbstractDisplayMeta.class, meta -> {
95 | Transform transform = new Transform();
96 | collisionObject.getTransform(transform);
97 |
98 | meta.setTransformationInterpolationDuration(1);
99 | meta.setPosRotInterpolationDuration(1);
100 | meta.setTransformationInterpolationStartDelta(0);
101 |
102 | entity.teleport(toPos(transform.getTranslation()));
103 |
104 | // size not updated as it doesn't change
105 | meta.setLeftRotation(toFloats(transform.getRotation()));
106 | });
107 | }
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/objects/RagdollPhysics.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.objects;
2 |
3 | import com.jme3.bullet.RotationOrder;
4 | import com.jme3.bullet.joints.New6Dof;
5 | import com.jme3.bullet.objects.PhysicsRigidBody;
6 | import com.jme3.math.Matrix3f;
7 | import com.jme3.math.Vector3f;
8 | import dev.emortal.MinecraftPhysics;
9 | import dev.emortal.NoTickingEntity;
10 | import dev.emortal.PlayerDisplayPart;
11 | import net.minestom.server.coordinate.Vec;
12 | import net.minestom.server.entity.Entity;
13 | import net.minestom.server.entity.EntityType;
14 | import net.minestom.server.entity.Player;
15 | import net.minestom.server.entity.PlayerSkin;
16 | import net.minestom.server.entity.metadata.display.ItemDisplayMeta;
17 | import net.minestom.server.instance.block.Block;
18 | import net.minestom.server.item.ItemComponent;
19 | import net.minestom.server.item.ItemStack;
20 | import net.minestom.server.item.Material;
21 | import net.minestom.server.item.component.HeadProfile;
22 | import org.jetbrains.annotations.NotNull;
23 | import org.jetbrains.annotations.Nullable;
24 |
25 | import java.util.List;
26 |
27 | import static dev.emortal.commands.PlayerSizeCommand.PLAYER_SIZE;
28 | import static dev.emortal.utils.CoordinateUtils.toVec;
29 | import static dev.emortal.utils.CoordinateUtils.toVector3;
30 |
31 | public class RagdollPhysics extends BlockRigidBody {
32 |
33 | private final @Nullable PlayerSkin playerSkin;
34 |
35 | private final @NotNull PlayerDisplayPart part;
36 |
37 | public RagdollPhysics(MinecraftPhysics mcPhysics, @NotNull Player spawner, @Nullable PhysicsRigidBody torso, @NotNull PlayerDisplayPart part, Vector3f position, Vec size, float mass) {
38 | super(mcPhysics, position, size, mass, true, Block.AIR);
39 | this.part = part;
40 |
41 | this.playerSkin = spawner.getSkin();
42 |
43 | Vec torsoSize = new Vec(8.0/16.0, 12.0/16.0, 4.0/16.0);
44 | Vec headSize = new Vec(8.0/16.0, 8.0/16.0, 8.0/16.0);
45 | Vec limbSize = new Vec(4.0/16.0, 12.0/16.0, 4.0/16.0);
46 |
47 | var rigidBody = (PhysicsRigidBody) getCollisionObject();
48 | rigidBody.setAngularDamping(0.1f);
49 | rigidBody.setLinearDamping(0.3f);
50 | rigidBody.setPhysicsLocation(position);
51 |
52 | if (torso != null) {
53 | assert (part != PlayerDisplayPart.TORSO);
54 |
55 | // From torso
56 | Vector3f firstThing = switch (part) {
57 | case HEAD -> new Vector3f(0f, (float)torsoSize.y() / 2f, 0f);
58 | case RIGHT_ARM -> new Vector3f((float)torsoSize.x() / 1.35f, (float)torsoSize.y() / 2f, 0f);
59 | case LEFT_ARM -> new Vector3f((float)-torsoSize.x() / 1.35f, (float)torsoSize.y() / 2f, 0f);
60 | case RIGHT_LEG -> new Vector3f(0.13f, (float)-torsoSize.y() / 2f, 0f);
61 | case LEFT_LEG -> new Vector3f(-0.13f, (float)-torsoSize.y() / 2f, 0f);
62 | default -> throw new IllegalStateException("Unexpected value: " + part);
63 | };
64 | // From part
65 | Vector3f secondThing = switch (part) {
66 | case HEAD -> new Vector3f(0.0f, (float)-headSize.y() / 2f, 0f);
67 | case RIGHT_ARM -> new Vector3f(0, (float)limbSize.y() / 2f, 0f);
68 | case LEFT_ARM -> new Vector3f(0, (float)limbSize.y() / 2f, 0f);
69 | case RIGHT_LEG -> new Vector3f(0f, (float)limbSize.y() / 2f, 0f);
70 | case LEFT_LEG -> new Vector3f(0f, (float)limbSize.y() / 2f, 0f);
71 | default -> throw new IllegalStateException("Unexpected value: " + part);
72 | };
73 |
74 | New6Dof joint = new New6Dof(torso, rigidBody, toVector3(toVec(firstThing).mul(PLAYER_SIZE)), toVector3(toVec(secondThing).mul(PLAYER_SIZE)), Matrix3f.IDENTITY, Matrix3f.IDENTITY, RotationOrder.XYZ);
75 | joint.setBreakingImpulseThreshold(60);
76 | mcPhysics.getPhysicsSpace().add(joint);
77 | addRelated(joint);
78 | }
79 | }
80 |
81 | @Override
82 | public Entity createEntity() {
83 | // Uses an ITEM_DISPLAY instead of a BLOCK_DISPLAY as it is centered around the middle instead of the corner
84 | // although causes issues with certain items, it works for most
85 | Entity entity = new NoTickingEntity(EntityType.ITEM_DISPLAY);
86 |
87 | entity.setBoundingBox(getSize().x(), getSize().y(), getSize().z());
88 |
89 | entity.editEntityMeta(ItemDisplayMeta.class, meta -> {
90 | meta.setWidth(2);
91 | meta.setHeight(2);
92 | meta.setDisplayContext(ItemDisplayMeta.DisplayContext.THIRD_PERSON_RIGHT_HAND);
93 | meta.setItemStack(
94 | ItemStack.builder(Material.PLAYER_HEAD)
95 | .itemModel(this.part.getCustomModelData())
96 | .set(ItemComponent.PROFILE, new HeadProfile(this.playerSkin))
97 | .customModelData(List.of(), List.of(), List.of("default"), List.of())
98 | .build()
99 | );
100 | meta.setScale(new Vec(PLAYER_SIZE));
101 | meta.setTranslation(new Vec(0, this.part.getYTranslation(), 0));
102 |
103 | // meta.setItemStack(ItemStack.of(Material.DIAMOND_BLOCK));
104 | // meta.setScale(getSize().mul(2));
105 | });
106 |
107 | return entity;
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/objects/TrapdoorPhysics.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.objects;
2 |
3 | //public class TrapdoorPhysics implements MinecraftPhysicsObject {
4 | //
5 | // private final Vector3f size;
6 | // private Entity entity;
7 | // private ItemDisplayMeta meta = null;
8 | //
9 | //
10 | // private final MinecraftPhysics physicsHandler;
11 | // private final PhysicsRigidBody rigidBody;
12 | // private final PhysicsJoint joint;
13 | //
14 | // public TrapdoorPhysics(MinecraftPhysics physicsHandler, @Nullable PhysicsRigidBody parent, Instance instance, Vector3f size, float mass, Vector3f jointPos) {
15 | // this.size = size;
16 | // this.physicsHandler = physicsHandler;
17 | //
18 | // physicsHandler.objects.add(this);
19 | //
20 | // BoxCollisionShape boxShape = new BoxCollisionShape(size.x, size.y, size.z);
21 | // rigidBody = new PhysicsRigidBody(boxShape, mass);
22 | // physicsHandler.getPhysicsSpace().addCollisionObject(rigidBody);
23 | // rigidBody.setAngularDamping(0.1f);
24 | // rigidBody.setLinearDamping(0.3f);
25 | // rigidBody.setPhysicsLocation(jointPos);
26 | // rigidBody.setPhysicsLocation(jointPos.subtract(0, 1f, 0f));
27 | //
28 | // if (parent == null) {
29 | // CollisionShape parentBoxShape = new BoxCollisionShape(0.1f);
30 | // PhysicsRigidBody parentlessRigidBody = new PhysicsRigidBody(parentBoxShape, PhysicsRigidBody.massForStatic);
31 | // parentlessRigidBody.setPhysicsLocation(jointPos);
32 | // physicsHandler.getPhysicsSpace().addCollisionObject(parentlessRigidBody);
33 | // parent = parentlessRigidBody;
34 | // }
35 | //
36 | // joint = new HingeJoint(parent, rigidBody, new Vector3f(0f, 0.5f, 0f), new Vector3f(0f, -0.5f, 0f), Vector3f.UNIT_X, Vector3f.UNIT_X);
37 | // physicsHandler.getPhysicsSpace().addJoint(joint);
38 | //
39 | // entity = spawnEntity(instance);
40 | // }
41 | //
42 | // private Entity spawnEntity(Instance instance) {
43 | // // Uses an ITEM_DISPLAY instead of a BLOCK_DISPLAY as it is centered around the middle instead of the corner
44 | // Entity entity = new NoTickingEntity(EntityType.ITEM_DISPLAY);
45 | //
46 | // entity.setBoundingBox(size.x, size.y, size.x);
47 | //
48 | // Transform transform = new Transform();
49 | // rigidBody.getTransform(transform);
50 | //
51 | // meta = (ItemDisplayMeta) entity.getEntityMeta();
52 | // meta.setNotifyAboutChanges(false);
53 | // meta.setWidth(2);
54 | // meta.setHeight(2);
55 | //
56 | // meta.setItemStack(ItemStack.of(Block.DIAMOND_BLOCK.registry().material()));
57 | // meta.setScale(toVec(transform.getScale()).mul(size.x * 2, size.y * 2, size.z * 2));
58 | //
59 | // meta.setLeftRotation(toFloats(transform.getRotation()));
60 | // meta.setNotifyAboutChanges(true);
61 | //
62 | // entity.setInstance(instance, toPos(transform.getTranslation()));
63 | //
64 | // return entity;
65 | // }
66 | //
67 | // @Override
68 | // public void updateEntity() {
69 | // if (meta == null) return;
70 | // if (entity == null) return;
71 | //
72 | // rigidBody.activate(); // Rigid bodies have to be constantly active in order to be pushed by the player
73 | // // Lanterns stop weirdly when this is off
74 | //
75 | // Transform transform = new Transform();
76 | // rigidBody.getTransform(transform);
77 | //
78 | // meta.setNotifyAboutChanges(false);
79 | // meta.setTransformationInterpolationDuration(1);
80 | // meta.setPosRotInterpolationDuration(1);
81 | // meta.setTransformationInterpolationStartDelta(0);
82 | //
83 | // entity.teleport(toPos(transform.getTranslation()));
84 | //
85 | // // size not updated as it doesn't change
86 | // meta.setLeftRotation(toFloats(transform.getRotation()));
87 | // meta.setNotifyAboutChanges(true);
88 | // }
89 | //
90 | // @Override
91 | // public @NotNull PhysicsRigidBody getRigidBody() {
92 | // return rigidBody;
93 | // }
94 | // @Override
95 | // public @Nullable Entity getEntity() {
96 | // return entity;
97 | // }
98 | // @Override
99 | // public @Nullable ItemDisplayMeta getMeta() {
100 | // return meta;
101 | // }
102 | //
103 | // @Override
104 | // public void destroy() {
105 | // if (entity != null) entity.remove();
106 | // entity = null;
107 | //
108 | // physicsHandler.objects.remove(this);
109 | // physicsHandler.getPhysicsSpace().removeCollisionObject(rigidBody);
110 | // physicsHandler.getPhysicsSpace().removeJoint(joint);
111 | // }
112 | //
113 | //}
114 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/tools/DeleteTool.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.tools;
2 |
3 | import com.jme3.bullet.collision.PhysicsCollisionObject;
4 | import com.jme3.bullet.collision.PhysicsRayTestResult;
5 | import dev.emortal.MinecraftPhysics;
6 | import dev.emortal.objects.MinecraftPhysicsObject;
7 | import net.kyori.adventure.sound.Sound;
8 | import net.kyori.adventure.text.Component;
9 | import net.kyori.adventure.text.format.NamedTextColor;
10 | import net.kyori.adventure.text.format.TextDecoration;
11 | import net.minestom.server.entity.Player;
12 | import net.minestom.server.item.ItemStack;
13 | import net.minestom.server.item.Material;
14 | import net.minestom.server.sound.SoundEvent;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | import java.util.List;
18 |
19 | import static dev.emortal.Main.raycastEntity;
20 |
21 | public class DeleteTool extends Tool {
22 |
23 | private final @NotNull Player player;
24 | private final @NotNull MinecraftPhysics physicsHandler;
25 | public DeleteTool(@NotNull Player player, @NotNull MinecraftPhysics physicsHandler) {
26 | super(player, "remover");
27 | this.player = player;
28 | this.physicsHandler = physicsHandler;
29 | }
30 |
31 |
32 | @Override
33 | void onSwitchHands() {
34 |
35 | }
36 |
37 | @Override
38 | public void onLeftClick() {
39 |
40 | }
41 |
42 | @Override
43 | public void onRightClick() {
44 | List results = raycastEntity(physicsHandler.getPhysicsSpace(), player.getPosition().add(0, player.getEyeHeight(), 0), player.getPosition().direction(), 1000);
45 | if (results.isEmpty()) return;
46 |
47 | PhysicsCollisionObject obj = results.getFirst().getCollisionObject();
48 |
49 | if (obj == null) return;
50 |
51 | MinecraftPhysicsObject mcObj = physicsHandler.getObjectByPhysicsObject(obj);
52 | if (mcObj != null) {
53 | player.playSound(Sound.sound(SoundEvent.BLOCK_STONE_BREAK, Sound.Source.MASTER, 0.5f, 0.6f), Sound.Emitter.self());
54 | mcObj.destroy();
55 | }
56 | }
57 |
58 | @Override
59 | public ItemStack getItem() {
60 | return ItemStack.builder(Material.TNT_MINECART)
61 | .customName(Component.text("Remover", NamedTextColor.RED).decoration(TextDecoration.ITALIC, false))
62 | .set(Tool.TOOL_NAME_TAG, "remover")
63 | .build();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/tools/DiamondLayerTool.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.tools;
2 |
3 | import com.jme3.math.Vector3f;
4 | import dev.emortal.MinecraftPhysics;
5 | import dev.emortal.objects.BlockRigidBody;
6 | import net.kyori.adventure.sound.Sound;
7 | import net.kyori.adventure.text.Component;
8 | import net.kyori.adventure.text.format.NamedTextColor;
9 | import net.kyori.adventure.text.format.TextDecoration;
10 | import net.minestom.server.coordinate.Vec;
11 | import net.minestom.server.entity.Player;
12 | import net.minestom.server.instance.block.Block;
13 | import net.minestom.server.item.ItemStack;
14 | import net.minestom.server.item.Material;
15 | import net.minestom.server.sound.SoundEvent;
16 | import org.jetbrains.annotations.NotNull;
17 |
18 | public class DiamondLayerTool extends Tool {
19 |
20 | private final @NotNull Player player;
21 | private final @NotNull MinecraftPhysics physicsHandler;
22 | public DiamondLayerTool(@NotNull Player player, @NotNull MinecraftPhysics physicsHandler) {
23 | super(player, "diamond");
24 | this.player = player;
25 | this.physicsHandler = physicsHandler;
26 | }
27 |
28 |
29 | @Override
30 | void onSwitchHands() {
31 |
32 | }
33 |
34 | @Override
35 | public void onLeftClick() {
36 |
37 | }
38 |
39 | @Override
40 | public void onRightClick() {
41 | player.playSound(Sound.sound(SoundEvent.ENTITY_VILLAGER_AMBIENT, Sound.Source.MASTER, 1f, 1f), Sound.Emitter.self());
42 |
43 | for (int x = -5; x <= 5; x++) {
44 | for (int z = -5; z <= 5; z++) {
45 | BlockRigidBody rigidBody = new BlockRigidBody(physicsHandler, new Vector3f(x, 20, z), new Vec(0.5), 1, true, Block.DIAMOND_BLOCK);
46 | rigidBody.setInstance();
47 | }
48 | }
49 | }
50 |
51 | @Override
52 | public ItemStack getItem() {
53 | return ItemStack.builder(Material.DIAMOND_BLOCK)
54 | .customName(Component.text("Diamond Spawner", NamedTextColor.LIGHT_PURPLE).decoration(TextDecoration.ITALIC, false))
55 | .set(Tool.TOOL_NAME_TAG, "diamond")
56 | .build();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/tools/GrabberTool.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.tools;
2 |
3 | import com.jme3.bullet.collision.PhysicsCollisionObject;
4 | import com.jme3.bullet.collision.PhysicsRayTestResult;
5 | import com.jme3.bullet.collision.shapes.CollisionShape;
6 | import com.jme3.bullet.collision.shapes.EmptyShape;
7 | import com.jme3.bullet.joints.ConeJoint;
8 | import com.jme3.bullet.joints.PhysicsJoint;
9 | import com.jme3.bullet.objects.PhysicsRigidBody;
10 | import com.jme3.math.Vector3f;
11 | import dev.emortal.MinecraftPhysics;
12 | import dev.emortal.objects.MinecraftPhysicsObject;
13 | import net.kyori.adventure.sound.Sound;
14 | import net.kyori.adventure.text.Component;
15 | import net.kyori.adventure.text.format.NamedTextColor;
16 | import net.kyori.adventure.text.format.TextDecoration;
17 | import net.minestom.server.coordinate.Pos;
18 | import net.minestom.server.coordinate.Vec;
19 | import net.minestom.server.entity.Player;
20 | import net.minestom.server.item.ItemStack;
21 | import net.minestom.server.item.Material;
22 | import net.minestom.server.network.packet.server.play.ParticlePacket;
23 | import net.minestom.server.particle.Particle;
24 | import net.minestom.server.sound.SoundEvent;
25 | import net.minestom.server.timer.Task;
26 | import net.minestom.server.timer.TaskSchedule;
27 | import org.jetbrains.annotations.NotNull;
28 | import org.jetbrains.annotations.Nullable;
29 |
30 | import java.util.HashMap;
31 | import java.util.List;
32 | import java.util.Map;
33 |
34 | import static dev.emortal.Main.raycastEntity;
35 | import static dev.emortal.utils.CoordinateUtils.toVec;
36 | import static dev.emortal.utils.CoordinateUtils.toVector3;
37 |
38 | public class GrabberTool extends Tool {
39 |
40 | private final double grabberForce = 7;
41 |
42 | private double holdingDistance = 0.0;
43 | private @Nullable PhysicsRigidBody heldObject = null;
44 | private @Nullable Task holdingTask = null;
45 |
46 | private final Map jointMap = new HashMap<>();
47 |
48 | private final @NotNull Player player;
49 | private final @NotNull MinecraftPhysics physicsHandler;
50 | public GrabberTool(@NotNull Player player, @NotNull MinecraftPhysics physicsHandler) {
51 | super(player, "grabber");
52 | this.player = player;
53 | this.physicsHandler = physicsHandler;
54 | }
55 |
56 |
57 | @Override
58 | boolean onSlotChange(int newSlot, int diff) {
59 | if (holdingTask == null) return false;
60 |
61 | holdingDistance += diff * 1.5;
62 |
63 | player.playSound(Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_HAT, Sound.Source.MASTER, 0.5f, 2f), Sound.Emitter.self());
64 |
65 | return true;
66 | }
67 |
68 | @Override
69 | void onSwitchHands() {
70 |
71 | }
72 |
73 | @Override
74 | public void onLeftClick() {
75 | if (holdingTask == null || heldObject == null) return;
76 |
77 | MinecraftPhysicsObject mcObj = physicsHandler.getObjectByPhysicsObject(heldObject);
78 | if (mcObj != null && mcObj.getEntity() != null) {
79 | mcObj.getEntity().setGlowing(false);
80 | }
81 |
82 | player.playSound(Sound.sound(SoundEvent.BLOCK_AMETHYST_BLOCK_PLACE, Sound.Source.MASTER, 0.5f, 1.8f), Sound.Emitter.self());
83 | player.playSound(Sound.sound(SoundEvent.ENTITY_BEE_STING, Sound.Source.MASTER, 0.5f, 2f), Sound.Emitter.self());
84 |
85 | Vector3f physicsLoc = new Vector3f();
86 | heldObject.getPhysicsLocation(physicsLoc);
87 |
88 | player.sendPacket(new ParticlePacket(Particle.REVERSE_PORTAL, toVec(physicsLoc), Pos.ZERO, 2.5f, 20));
89 |
90 | CollisionShape jointBoxShape = new EmptyShape(false);
91 | PhysicsRigidBody jointRigidBody = new PhysicsRigidBody(jointBoxShape, PhysicsRigidBody.massForStatic);
92 | jointRigidBody.setPhysicsLocation(physicsLoc);
93 | physicsHandler.getPhysicsSpace().add(jointRigidBody);
94 | ConeJoint joint = new ConeJoint(heldObject, jointRigidBody, Vector3f.ZERO, Vector3f.ZERO);
95 | physicsHandler.getPhysicsSpace().addJoint(joint);
96 |
97 | jointMap.put(heldObject, jointRigidBody);
98 |
99 | holdingTask.cancel();
100 | holdingTask = null;
101 | heldObject = null;
102 | }
103 |
104 | @Override
105 | public void onRightClick() {
106 | if (holdingTask != null) {
107 | MinecraftPhysicsObject mcObj = physicsHandler.getObjectByPhysicsObject(heldObject);
108 | if (mcObj != null && mcObj.getEntity() != null) {
109 | mcObj.getEntity().setGlowing(false);
110 | }
111 |
112 | holdingTask.cancel();
113 | holdingTask = null;
114 | heldObject = null;
115 |
116 | player.playSound(Sound.sound(SoundEvent.BLOCK_AMETHYST_BLOCK_PLACE, Sound.Source.MASTER, 0.5f, 1.8f), Sound.Emitter.self());
117 | return;
118 | }
119 |
120 | List results = raycastEntity(physicsHandler.getPhysicsSpace(), player.getPosition().add(0, player.getEyeHeight(), 0), player.getPosition().direction(), 1000);
121 | if (results.isEmpty()) return;
122 |
123 | PhysicsCollisionObject obj = results.getFirst().getCollisionObject();
124 | if (!(obj instanceof PhysicsRigidBody rigidBody)) return;
125 |
126 | if (jointMap.containsKey(obj)) { // Remove holding joints if any
127 | PhysicsRigidBody joint = jointMap.get(obj);
128 | for (PhysicsJoint physicsJoint : joint.listJoints()) {
129 | physicsHandler.getPhysicsSpace().remove(physicsJoint);
130 | }
131 | physicsHandler.getPhysicsSpace().remove(joint);
132 |
133 | jointMap.remove(obj);
134 | }
135 |
136 | player.playSound(Sound.sound(SoundEvent.BLOCK_AMETHYST_BLOCK_PLACE, Sound.Source.MASTER, 0.5f, 2f), Sound.Emitter.self());
137 |
138 | rigidBody.activate();
139 | heldObject = rigidBody;
140 |
141 | Vector3f objPos = new Vector3f();
142 | obj.getPhysicsLocation(objPos);
143 | holdingDistance = player.getPosition().distance(toVec(objPos));
144 |
145 | MinecraftPhysicsObject mcObj = physicsHandler.getObjectByPhysicsObject(obj);
146 | if (mcObj != null && mcObj.getEntity() != null) {
147 | mcObj.getEntity().setGlowing(true);
148 | }
149 |
150 | holdingTask = player.scheduler().buildTask(() -> {
151 | Vector3f physicsVec = new Vector3f();
152 | obj.getPhysicsLocation(physicsVec);
153 |
154 | Vec wantedPos = Vec.fromPoint(player.getPosition().add(0, player.getEyeHeight(), 0).add(player.getPosition().direction().mul(holdingDistance)));
155 | Vec diff = Vec.fromPoint(wantedPos.sub(toVec(physicsVec)));
156 |
157 | rigidBody.setLinearVelocity(toVector3(diff.mul(grabberForce)));
158 | }).repeat(TaskSchedule.tick(1)).schedule();
159 | }
160 |
161 | @Override
162 | public ItemStack getItem() {
163 | return ItemStack.builder(Material.BLAZE_ROD)
164 | .customName(Component.text("Grabber", NamedTextColor.GOLD).decoration(TextDecoration.ITALIC, false))
165 | .set(Tool.TOOL_NAME_TAG, "grabber")
166 | .build();
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/tools/PlayerSpawnerTool.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.tools;
2 |
3 | import com.jme3.bullet.objects.PhysicsRigidBody;
4 | import dev.emortal.MinecraftPhysics;
5 | import dev.emortal.PlayerDisplayPart;
6 | import dev.emortal.objects.MinecraftPhysicsObject;
7 | import dev.emortal.objects.RagdollPhysics;
8 | import net.kyori.adventure.sound.Sound;
9 | import net.kyori.adventure.text.Component;
10 | import net.kyori.adventure.text.format.NamedTextColor;
11 | import net.kyori.adventure.text.format.TextDecoration;
12 | import net.minestom.server.coordinate.Pos;
13 | import net.minestom.server.coordinate.Vec;
14 | import net.minestom.server.entity.Player;
15 | import net.minestom.server.instance.Instance;
16 | import net.minestom.server.item.ItemComponent;
17 | import net.minestom.server.item.ItemStack;
18 | import net.minestom.server.item.Material;
19 | import net.minestom.server.item.component.HeadProfile;
20 | import net.minestom.server.sound.SoundEvent;
21 | import org.jetbrains.annotations.NotNull;
22 |
23 | import static dev.emortal.commands.PlayerSizeCommand.PLAYER_SIZE;
24 | import static dev.emortal.utils.CoordinateUtils.toVector3;
25 |
26 | public class PlayerSpawnerTool extends Tool {
27 |
28 | private final @NotNull Player player;
29 | private final @NotNull MinecraftPhysics physicsHandler;
30 | public PlayerSpawnerTool(@NotNull Player player, @NotNull MinecraftPhysics physicsHandler) {
31 | super(player, "playerspawner");
32 | this.player = player;
33 | this.physicsHandler = physicsHandler;
34 | }
35 |
36 |
37 | @Override
38 | void onSwitchHands() {
39 |
40 | }
41 |
42 | @Override
43 | public void onLeftClick() {
44 |
45 | }
46 |
47 | @Override
48 | public void onRightClick() {
49 | player.playSound(Sound.sound(SoundEvent.ENTITY_VILLAGER_AMBIENT, Sound.Source.MASTER, 1f, 1f), Sound.Emitter.self());
50 |
51 | Instance instance = player.getInstance();
52 | Pos startPos = player.getPosition().add(0, player.getEyeHeight(), 0).add(player.getPosition().direction().mul(5));
53 | // all halves \/
54 | Vec torsoSize = new Vec(4.0f/16.0f, 6.0f/16.0f, 2.0f/16.0f).mul(PLAYER_SIZE);
55 | // Vector3 headSize = new Vector3(4.0f/16.0f, 4.0f/16.0f, 4.0f/16.0f);
56 | Vec headSize = new Vec(3f/16.0f, 3.0f/16.0f, 3f/16.0f).mul(PLAYER_SIZE);
57 | // Vector3 limbSize = new Vector3(2.0f/16.0f, 6.0f/16.0f, 2.0f/16.0f);
58 | Vec limbSize = new Vec(1f/16.0f, 6.0f/16.0f, 1f/16.0f).mul(PLAYER_SIZE);
59 |
60 | MinecraftPhysicsObject torso = new RagdollPhysics(physicsHandler, player,null, PlayerDisplayPart.TORSO, toVector3(startPos), torsoSize, 1);
61 | PhysicsRigidBody torsoBody = (PhysicsRigidBody) torso.getCollisionObject();
62 | MinecraftPhysicsObject head = new RagdollPhysics(physicsHandler, player, torsoBody, PlayerDisplayPart.HEAD, toVector3(startPos.add(new Vec(0, 0.62, 0).mul(PLAYER_SIZE))), headSize, 1);
63 | MinecraftPhysicsObject rightArm = new RagdollPhysics(physicsHandler, player, torsoBody, PlayerDisplayPart.RIGHT_ARM, toVector3(startPos.add(new Vec(0.37, 0, 0).mul(PLAYER_SIZE))), limbSize, 1);
64 | MinecraftPhysicsObject leftArm = new RagdollPhysics(physicsHandler, player, torsoBody, PlayerDisplayPart.LEFT_ARM, toVector3(startPos.add(new Vec(-0.37, 0, 0).mul(PLAYER_SIZE))), limbSize, 1);
65 | MinecraftPhysicsObject rightLeg = new RagdollPhysics(physicsHandler, player, torsoBody, PlayerDisplayPart.RIGHT_LEG, toVector3(startPos.add(new Vec(0.13, -0.72, 0).mul(PLAYER_SIZE))), limbSize, 1);
66 | MinecraftPhysicsObject leftLeg = new RagdollPhysics(physicsHandler, player, torsoBody, PlayerDisplayPart.LEFT_LEG, toVector3(startPos.add(new Vec(-0.13, -0.72, 0).mul(PLAYER_SIZE))), limbSize, 1);
67 | // Main.paused = true;
68 |
69 | torso.setInstance();
70 | head.setInstance();
71 | rightArm.setInstance();
72 | leftArm.setInstance();
73 | rightLeg.setInstance();
74 | leftLeg.setInstance();
75 | }
76 |
77 | @Override
78 | public ItemStack getItem() {
79 | return ItemStack.builder(Material.PLAYER_HEAD)
80 | .set(ItemComponent.PROFILE, new HeadProfile(player.getSkin()))
81 | .customName(Component.text("Player Spawner", NamedTextColor.LIGHT_PURPLE).decoration(TextDecoration.ITALIC, false))
82 | .set(Tool.TOOL_NAME_TAG, "playerspawner")
83 | .build();
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/tools/Tool.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.tools;
2 |
3 | import net.minestom.server.entity.Player;
4 | import net.minestom.server.event.player.*;
5 | import net.minestom.server.item.ItemStack;
6 | import net.minestom.server.tag.Tag;
7 |
8 | public abstract class Tool {
9 |
10 | public static final Tag TOOL_NAME_TAG = Tag.String("toolName");
11 |
12 | public Tool(Player player, String toolName) {
13 |
14 | player.eventNode().addListener(PlayerChangeHeldSlotEvent.class, e -> {
15 | ItemStack item = e.getPlayer().getInventory().getItemStack(e.getOldSlot());
16 | if (!item.hasTag(TOOL_NAME_TAG)) return;
17 | String toolNameTag = item.getTag(TOOL_NAME_TAG);
18 | if (!toolNameTag.equals(toolName)) return;
19 |
20 | boolean shouldCancel = onSlotChange(e.getNewSlot(), e.getOldSlot() - e.getNewSlot());
21 |
22 | e.setCancelled(shouldCancel);
23 | });
24 | player.eventNode().addListener(PlayerBlockPlaceEvent.class, e -> {
25 | ItemStack item = e.getPlayer().getItemInHand(e.getHand());
26 | if (!item.hasTag(TOOL_NAME_TAG)) return;
27 | String toolNameTag = item.getTag(TOOL_NAME_TAG);
28 | if (!toolNameTag.equals(toolName)) return;
29 |
30 | e.setCancelled(true);
31 |
32 | onRightClick();
33 | });
34 | player.eventNode().addListener(PlayerUseItemEvent.class, e -> {
35 | if (!e.getItemStack().hasTag(TOOL_NAME_TAG)) return;
36 | String toolNameTag = e.getItemStack().getTag(TOOL_NAME_TAG);
37 | if (!toolNameTag.equals(toolName)) return;
38 |
39 | onRightClick();
40 | });
41 | player.eventNode().addListener(PlayerHandAnimationEvent.class, e -> {
42 | ItemStack item = e.getPlayer().getItemInHand(e.getHand());
43 |
44 | if (!item.hasTag(TOOL_NAME_TAG)) return;
45 | String toolNameTag = item.getTag(TOOL_NAME_TAG);
46 | if (!toolNameTag.equals(toolName)) return;
47 |
48 | onLeftClick();
49 | });
50 | player.eventNode().addListener(PlayerSwapItemEvent.class, e -> {
51 | String toolNameTagMain = e.getMainHandItem().getTag(TOOL_NAME_TAG);
52 | if (toolNameTagMain == null) toolNameTagMain = "";
53 | String toolNameTagOff = e.getOffHandItem().getTag(TOOL_NAME_TAG);
54 | if (toolNameTagOff == null) toolNameTagOff = "";
55 | if (!toolNameTagMain.equals(toolName) && !toolNameTagOff.equals(toolName)) return;
56 |
57 | onSwitchHands();
58 |
59 | e.setCancelled(true);
60 | });
61 |
62 | }
63 |
64 | abstract void onSwitchHands();
65 | abstract void onLeftClick();
66 | abstract void onRightClick();
67 | boolean onSlotChange(int newSlot, int diff) {
68 | return false;
69 | }
70 |
71 | abstract ItemStack getItem();
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/tools/WeldTool.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.tools;
2 |
3 | import com.jme3.bullet.collision.PhysicsCollisionObject;
4 | import com.jme3.bullet.collision.PhysicsRayTestResult;
5 | import com.jme3.bullet.joints.ConeJoint;
6 | import com.jme3.bullet.joints.PhysicsJoint;
7 | import com.jme3.bullet.objects.PhysicsRigidBody;
8 | import com.jme3.math.Vector3f;
9 | import dev.emortal.MinecraftPhysics;
10 | import net.kyori.adventure.sound.Sound;
11 | import net.kyori.adventure.text.Component;
12 | import net.kyori.adventure.text.format.NamedTextColor;
13 | import net.kyori.adventure.text.format.TextDecoration;
14 | import net.minestom.server.entity.Player;
15 | import net.minestom.server.item.ItemStack;
16 | import net.minestom.server.item.Material;
17 | import net.minestom.server.sound.SoundEvent;
18 | import org.jetbrains.annotations.NotNull;
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | import java.util.List;
22 |
23 | import static dev.emortal.Main.raycastEntity;
24 |
25 | public class WeldTool extends Tool {
26 |
27 | private @Nullable PhysicsRigidBody firstObject = null;
28 |
29 | private boolean keepDistance = false;
30 |
31 | private final @NotNull Player player;
32 | private final @NotNull MinecraftPhysics physicsHandler;
33 | public WeldTool(@NotNull Player player, @NotNull MinecraftPhysics physicsHandler) {
34 | super(player, "welder");
35 | this.player = player;
36 | this.physicsHandler = physicsHandler;
37 | }
38 |
39 | @Override
40 | void onSwitchHands() {
41 | player.sendMessage("Changed weld mode");
42 | keepDistance = !keepDistance;
43 | }
44 |
45 | @Override
46 | public void onLeftClick() {
47 | if (firstObject != null) {
48 | firstObject = null;
49 | player.sendMessage("Deselected first object");
50 | }
51 |
52 | List results = raycastEntity(physicsHandler.getPhysicsSpace(), player.getPosition().add(0, player.getEyeHeight(), 0), player.getPosition().direction(), 1000);
53 | if (results.isEmpty()) return;
54 |
55 | PhysicsCollisionObject obj = results.getFirst().getCollisionObject();
56 | if (!(obj instanceof PhysicsRigidBody rigidBody)) return;
57 |
58 | for (PhysicsJoint physicsJoint : rigidBody.listJoints()) {
59 | rigidBody.removeJoint(physicsJoint);
60 | }
61 | player.sendMessage("Removed weld");
62 | }
63 |
64 | @Override
65 | public void onRightClick() {
66 | List results = raycastEntity(physicsHandler.getPhysicsSpace(), player.getPosition().add(0, player.getEyeHeight(), 0), player.getPosition().direction(), 1000);
67 | if (results.isEmpty()) return;
68 |
69 | PhysicsCollisionObject obj = results.getFirst().getCollisionObject();
70 | if (!(obj instanceof PhysicsRigidBody rigidBody)) return;
71 |
72 | if (firstObject != null) {
73 | Vector3f firstObjectPos = new Vector3f();
74 | Vector3f secondObjectPos = new Vector3f();
75 |
76 | firstObject.getPhysicsLocation(firstObjectPos);
77 | obj.getPhysicsLocation(secondObjectPos);
78 |
79 | PhysicsJoint joint;
80 | if (keepDistance) {
81 | joint = new ConeJoint(firstObject, rigidBody, secondObjectPos.subtract(firstObjectPos), firstObjectPos.subtract(secondObjectPos));
82 | } else {
83 | joint = new ConeJoint(firstObject, rigidBody, Vector3f.ZERO, Vector3f.ZERO);
84 | }
85 | physicsHandler.getPhysicsSpace().addJoint(joint);
86 |
87 | firstObject.activate();
88 | rigidBody.activate();
89 |
90 | firstObject = null;
91 |
92 | player.playSound(Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_PLING, Sound.Source.MASTER, 0.5f, 2f), Sound.Emitter.self());
93 | return;
94 | }
95 | player.playSound(Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_PLING, Sound.Source.MASTER, 0.5f, 1.5f), Sound.Emitter.self());
96 | firstObject = rigidBody;
97 | }
98 |
99 | @Override
100 | public ItemStack getItem() {
101 | return ItemStack.builder(Material.NETHERITE_PICKAXE)
102 | .customName(Component.text("Welder", NamedTextColor.GOLD).decoration(TextDecoration.ITALIC, false))
103 | .set(Tool.TOOL_NAME_TAG, "welder")
104 | .build();
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/utils/CoordinateUtils.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.utils;
2 |
3 | import com.jme3.math.Quaternion;
4 | import com.jme3.math.Vector3f;
5 | import net.minestom.server.coordinate.Point;
6 | import net.minestom.server.coordinate.Pos;
7 | import net.minestom.server.coordinate.Vec;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | public class CoordinateUtils {
11 |
12 | private CoordinateUtils() {}
13 |
14 | public static @NotNull Vec toVec(Vector3f vector3) {
15 | return new Vec(vector3.x, vector3.y, vector3.z);
16 | }
17 | public static @NotNull Pos toPos(Vector3f vector3) {
18 | return new Pos(vector3.x, vector3.y, vector3.z);
19 | }
20 | public static @NotNull Vector3f toVector3(Point vec) {
21 | return new Vector3f((float)vec.x(), (float)vec.y(), (float)vec.z());
22 | }
23 | public static float[] toFloats(Quaternion rotation) {
24 | return new float[] { rotation.getX(), rotation.getY(), rotation.getZ(), rotation.getW() };
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/dev/emortal/utils/QuaternionCustom.java:
--------------------------------------------------------------------------------
1 | package dev.emortal.utils;
2 |
3 | import com.jme3.math.FastMath;
4 | import net.minestom.server.coordinate.Vec;
5 | import org.jetbrains.annotations.NotNull;
6 |
7 | public final class QuaternionCustom {
8 |
9 | private double x;
10 | private double y;
11 | private double z;
12 | private double w;
13 |
14 | public QuaternionCustom(@NotNull QuaternionCustom q) {
15 | this(q.x, q.y, q.z, q.w);
16 | }
17 |
18 | public QuaternionCustom(double x, double y, double z, double w) {
19 | this.x = x;
20 | this.y = y;
21 | this.z = z;
22 | this.w = w;
23 | }
24 |
25 | public void set(@NotNull QuaternionCustom q) {
26 | this.x = q.x;
27 | this.y = q.y;
28 | this.z = q.z;
29 | this.w = q.w;
30 | }
31 |
32 | public QuaternionCustom(@NotNull Vec axis, double angle) {
33 | this.set(axis, angle);
34 | }
35 |
36 | public double norm() {
37 | return Math.sqrt(this.dot(this));
38 | }
39 |
40 | public double getW() {
41 | return this.w;
42 | }
43 |
44 | public double getX() {
45 | return this.x;
46 | }
47 |
48 | public double getY() {
49 | return this.y;
50 | }
51 |
52 | public double getZ() {
53 | return this.z;
54 | }
55 |
56 | /**
57 | * @param axis
58 | * rotation axis, unit vector
59 | * @param angle
60 | * the rotation angle
61 | * @return this
62 | */
63 | public @NotNull QuaternionCustom set(@NotNull Vec axis, double angle) {
64 | double s = Math.sin(angle / 2);
65 | this.w = Math.cos(angle / 2);
66 | this.x = axis.x() * s;
67 | this.y = axis.y() * s;
68 | this.z = axis.z() * s;
69 | return this;
70 | }
71 |
72 | public @NotNull QuaternionCustom mulThis(@NotNull QuaternionCustom q) {
73 | double nw = this.w * q.w - this.x * q.x - this.y * q.y - this.z * q.z;
74 | double nx = this.w * q.x + this.x * q.w + this.y * q.z - this.z * q.y;
75 | double ny = this.w * q.y + this.y * q.w + this.z * q.x - this.x * q.z;
76 | this.z = this.w * q.z + this.z * q.w + this.x * q.y - this.y * q.x;
77 | this.w = nw;
78 | this.x = nx;
79 | this.y = ny;
80 | return this;
81 | }
82 |
83 | public @NotNull QuaternionCustom scaleThis(double scale) {
84 | if (scale != 1) {
85 | this.w *= scale;
86 | this.x *= scale;
87 | this.y *= scale;
88 | this.z *= scale;
89 | }
90 | return this;
91 | }
92 |
93 | public @NotNull QuaternionCustom divThis(double scale) {
94 | if (scale != 1) {
95 | this.w /= scale;
96 | this.x /= scale;
97 | this.y /= scale;
98 | this.z /= scale;
99 | }
100 | return this;
101 | }
102 |
103 | public double dot(@NotNull QuaternionCustom q) {
104 | return this.x * q.x + this.y * q.y + this.z * q.z + this.w * q.w;
105 | }
106 |
107 | public boolean equals(@NotNull QuaternionCustom q) {
108 | return this.x == q.x && this.y == q.y && this.z == q.z && this.w == q.w;
109 | }
110 |
111 | public @NotNull QuaternionCustom interpolateThis(@NotNull QuaternionCustom q, double t) {
112 | if (!this.equals(q)) {
113 | double d = this.dot(q);
114 | double qx, qy, qz, qw;
115 |
116 | if (d < 0f) {
117 | qx = -q.x;
118 | qy = -q.y;
119 | qz = -q.z;
120 | qw = -q.w;
121 | d = -d;
122 | } else {
123 | qx = q.x;
124 | qy = q.y;
125 | qz = q.z;
126 | qw = q.w;
127 | }
128 |
129 | double f0, f1;
130 |
131 | if ((1 - d) > 0.1f) {
132 | double angle = Math.acos(d);
133 | double s = Math.sin(angle);
134 | double tAngle = t * angle;
135 | f0 = Math.sin(angle - tAngle) / s;
136 | f1 = Math.sin(tAngle) / s;
137 | } else {
138 | f0 = 1 - t;
139 | f1 = t;
140 | }
141 |
142 | this.x = f0 * this.x + f1 * qx;
143 | this.y = f0 * this.y + f1 * qy;
144 | this.z = f0 * this.z + f1 * qz;
145 | this.w = f0 * this.w + f1 * qw;
146 | }
147 |
148 | return this;
149 | }
150 |
151 | public @NotNull QuaternionCustom normalizeThis() {
152 | return this.divThis(this.norm());
153 | }
154 |
155 | public @NotNull QuaternionCustom interpolate(@NotNull QuaternionCustom q, double t) {
156 | return new QuaternionCustom(this).interpolateThis(q, t);
157 | }
158 |
159 | /**
160 | * Converts this Quaternion into a matrix, returning it as a float array.
161 | */
162 | public float[] toMatrix() {
163 | float[] matrixs = new float[16];
164 | this.toMatrix(matrixs);
165 | return matrixs;
166 | }
167 |
168 | /**
169 | * Converts this Quaternion into a matrix, placing the values into the given array.
170 | * @param matrixs 16-length float array.
171 | */
172 | public void toMatrix(float[] matrixs) {
173 | matrixs[3] = 0.0f;
174 | matrixs[7] = 0.0f;
175 | matrixs[11] = 0.0f;
176 | matrixs[12] = 0.0f;
177 | matrixs[13] = 0.0f;
178 | matrixs[14] = 0.0f;
179 | matrixs[15] = 1.0f;
180 |
181 | matrixs[0] = (float) (1.0f - (2.0f * ((this.y * this.y) + (this.z * this.z))));
182 | matrixs[1] = (float) (2.0f * ((x * y) - (z * w)));
183 | matrixs[2] = (float) (2.0f * ((x * z) + (y * w)));
184 |
185 | matrixs[4] = (float) (2.0f * ((x * y) + (z * w)));
186 | matrixs[5] = (float) (1.0f - (2.0f * ((x * x) + (z * z))));
187 | matrixs[6] = (float) (2.0f * ((y * z) - (x * w)));
188 |
189 | matrixs[8] = (float) (2.0f * ((x * z) - (y * w)));
190 | matrixs[9] = (float) (2.0f * ((y * z) + (x * w)));
191 | matrixs[10] = (float) (1.0f - (2.0f * ((x * x) + (y * y))));
192 | }
193 |
194 | public QuaternionCustom(float yaw, float roll, float pitch) {
195 | float angle;
196 | float sinRoll, sinPitch, sinYaw, cosRoll, cosPitch, cosYaw;
197 | angle = pitch * 0.5f;
198 | sinPitch = FastMath.sin(angle);
199 | cosPitch = FastMath.cos(angle);
200 | angle = roll * 0.5f;
201 | sinRoll = FastMath.sin(angle);
202 | cosRoll = FastMath.cos(angle);
203 | angle = yaw * 0.5f;
204 | sinYaw = FastMath.sin(angle);
205 | cosYaw = FastMath.cos(angle);
206 |
207 | // variables used to reduce multiplication calls.
208 | float cosRollXcosPitch = cosRoll * cosPitch;
209 | float sinRollXsinPitch = sinRoll * sinPitch;
210 | float cosRollXsinPitch = cosRoll * sinPitch;
211 | float sinRollXcosPitch = sinRoll * cosPitch;
212 |
213 | w = (cosRollXcosPitch * cosYaw - sinRollXsinPitch * sinYaw);
214 | x = (cosRollXcosPitch * sinYaw + sinRollXsinPitch * cosYaw);
215 | y = (sinRollXcosPitch * cosYaw + cosRollXsinPitch * sinYaw);
216 | z = (cosRollXsinPitch * cosYaw - sinRollXcosPitch * sinYaw);
217 |
218 | normalizeThis();
219 | }
220 | }
--------------------------------------------------------------------------------