Compare commits
12 Commits
265d71ae67
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a9ce715750 | |||
| 7aefd13755 | |||
| 562a16c4f8 | |||
| b4582640b7 | |||
| 01ba9ca366 | |||
| 4ee1a066ac | |||
| 9545a46171 | |||
| a654eec852 | |||
| 0d1e607206 | |||
| 4aa2643a76 | |||
| b1cd170c60 | |||
| ec041f9269 |
69
build.gradle
69
build.gradle
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "com.modrinth.minotaur" version "2.+"
|
id "com.modrinth.minotaur" version "2.+"
|
||||||
id 'fabric-loom' version '1.2-SNAPSHOT'
|
id 'fabric-loom' version '1.7-SNAPSHOT'
|
||||||
id 'maven-publish'
|
id 'maven-publish'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,8 +18,8 @@ repositories {
|
|||||||
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
|
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
|
||||||
// for more information about repositories.
|
// for more information about repositories.
|
||||||
maven {
|
maven {
|
||||||
name = 'Ladysnake Mods'
|
name = "Ladysnake Mods"
|
||||||
url = 'https://ladysnake.jfrog.io/artifactory/mods'
|
url = 'https://maven.ladysnake.org/releases'
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
url = "https://api.modrinth.com/maven"
|
url = "https://api.modrinth.com/maven"
|
||||||
@@ -50,48 +50,27 @@ dependencies {
|
|||||||
|
|
||||||
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
|
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
|
||||||
|
|
||||||
// Dependency of Immersive Portals Core:
|
modImplementation ("com.github.iPortalTeam:ImmersivePortalsMod:${project.immersive_portals_version}")
|
||||||
modImplementation("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}") {
|
|
||||||
exclude(group: "net.fabricmc.fabric-api")
|
|
||||||
exclude(group: "net.fabricmc.fabric-loader")
|
|
||||||
transitive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dependency of the Miscellaneous Utility Library from qouteall
|
|
||||||
modImplementation("com.github.iPortalTeam.ImmersivePortalsMod:q_misc_util:${project.immersive_portals_version}") {
|
|
||||||
exclude(group: "net.fabricmc.fabric-api")
|
|
||||||
exclude(group: "net.fabricmc.fabric-loader")
|
|
||||||
transitive(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
include(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-base:${project.cardinal_components_version}")) {
|
|
||||||
exclude(group: "net.fabricmc.fabric-loader")
|
|
||||||
transitive(false)
|
|
||||||
}
|
|
||||||
// Replace modImplementation with modApi if you expose components in your own API
|
// Replace modImplementation with modApi if you expose components in your own API
|
||||||
include(modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-level:${project.cardinal_components_version}")) {
|
modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}"
|
||||||
exclude(group: "net.fabricmc.fabric-loader")
|
// Includes Cardinal Components API as a Jar-in-Jar dependency (optional but recommended)
|
||||||
transitive(false)
|
include "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}"
|
||||||
|
|
||||||
|
// Replace modImplementation with modApi if you expose components in your own API
|
||||||
|
modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-level:${project.cca_version}"
|
||||||
|
// Includes Cardinal Components API as a Jar-in-Jar dependency (optional but recommended)
|
||||||
|
include "org.ladysnake.cardinal-components-api:cardinal-components-level:${project.cca_version}"
|
||||||
|
|
||||||
|
//Config lib
|
||||||
|
modImplementation "maven.modrinth:midnightlib:${project.midnightlib_version}"
|
||||||
|
include "maven.modrinth:midnightlib:${project.midnightlib_version}"
|
||||||
|
|
||||||
|
modImplementation("maven.modrinth:sodium:${project.sodium_version}")
|
||||||
}
|
}
|
||||||
|
|
||||||
api("com.github.LlamaLad7:MixinExtras:0.2.0-beta.4")
|
loom {
|
||||||
annotationProcessor("com.github.LlamaLad7:MixinExtras:0.2.0-beta.4")
|
accessWidenerPath = file("src/main/resources/colourful_portals.accesswidener")
|
||||||
|
|
||||||
include(modApi(platform("de.siphalor.tweed4:tweed4-bom-$project.minecraft_version_major:$project.tweed_version")))
|
|
||||||
// Pick any modules you want to use, e.g.:
|
|
||||||
include(modApi("de.siphalor.tweed4:tweed4-base-$project.minecraft_version_major"))
|
|
||||||
include(modApi("de.siphalor.tweed4:tweed4-annotated-$project.minecraft_version_major"))
|
|
||||||
include(modApi("de.siphalor.tweed4:tweed4-data-$project.minecraft_version_major"))
|
|
||||||
include(modApi("de.siphalor.tweed4:tweed4-data-hjson-$project.minecraft_version_major"))
|
|
||||||
//include(modApi("de.siphalor.tweed4:tweed4-tailor-cloth-$project.minecraft_version_major"))
|
|
||||||
include(modApi("de.siphalor.tweed4:tweed4-tailor-screen-$project.minecraft_version_major"))
|
|
||||||
|
|
||||||
|
|
||||||
modImplementation("me.shedaniel.cloth:cloth-config-fabric:$project.cloth_config_version") {
|
|
||||||
exclude(group: "net.fabricmc.fabric-api")
|
|
||||||
}
|
|
||||||
|
|
||||||
modCompileOnly "maven.modrinth:sodium:mc${project.minecraft_version_major}-${project.sodium_version}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.include.transitive = true
|
configurations.include.transitive = true
|
||||||
@@ -109,7 +88,7 @@ processResources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JavaCompile).configureEach {
|
tasks.withType(JavaCompile).configureEach {
|
||||||
it.options.release = 17
|
it.options.release = 21
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -118,8 +97,8 @@ java {
|
|||||||
// If you remove this line, sources will not be generated.
|
// If you remove this line, sources will not be generated.
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_21
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_21
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@@ -136,7 +115,7 @@ modrinth {
|
|||||||
versionType = "beta"
|
versionType = "beta"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
new ModDependency('P7dR8mSH', 'required'), //required dependency on Fabric API
|
new ModDependency('P7dR8mSH', 'required'), //required dependency on Fabric API
|
||||||
new ModDependency('zJpHMkdD', 'required') //required dependency on Immersive Portals
|
new ModDependency('AANobbMI', 'optional'), //compatible with Sodium
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
// configure the maven publication
|
// configure the maven publication
|
||||||
|
|||||||
@@ -4,21 +4,20 @@ org.gradle.parallel=true
|
|||||||
|
|
||||||
# Fabric Properties
|
# Fabric Properties
|
||||||
# check these on https://fabricmc.net/develop
|
# check these on https://fabricmc.net/develop
|
||||||
minecraft_version=1.20.1
|
minecraft_version=1.21.1
|
||||||
yarn_mappings=1.20.1+build.8
|
minecraft_version_major=1.21
|
||||||
loader_version=0.14.21
|
yarn_mappings=1.21.1+build.3
|
||||||
minecraft_version_major=1.20
|
loader_version=0.16.7
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=0.9.3
|
mod_version=0.9.7
|
||||||
maven_group=quimufu.colourful-portals
|
maven_group=quimufu.colourful-portals
|
||||||
archives_base_name=colourful-portals
|
archives_base_name=colourful-portals
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
fabric_version=0.84.0+1.20.1
|
fabric_version=0.105.0+1.21.1
|
||||||
cardinal_components_version=5.2.1
|
cca_version = 6.1.1
|
||||||
immersive_portals_version_short=3.1.0
|
immersive_portals_version_short=6.0.3
|
||||||
immersive_portals_version=v3.1.0-mc1.20.1
|
immersive_portals_version=v6.0.3-mc1.21.1
|
||||||
tweed_version=1.3.0+mc1.20-pre1
|
sodium_version=mc1.21-0.5.11
|
||||||
sodium_version=0.4.10
|
midnightlib_version=1.6.3-fabric
|
||||||
cloth_config_version=11.0.99
|
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
24
gradlew
vendored
24
gradlew
vendored
@@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -83,7 +83,8 @@ done
|
|||||||
# This is normally unused
|
# This is normally unused
|
||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -130,18 +131,21 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
@@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
|||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
# shellcheck disable=SC3045
|
# shellcheck disable=SC2039,SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -198,11 +202,11 @@ fi
|
|||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command:
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# and any embedded shellness will be escaped.
|
||||||
# double quotes to make sure that they get re-expanded; and
|
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||||
# * put everything else in single quotes, so that it's not re-expanded.
|
# treated as '${Hostname}' itself on the command line.
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
|||||||
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
|||||||
153
src/main/java/quimufu/colourful_portals/ColourfulAirBlock.java
Normal file
153
src/main/java/quimufu/colourful_portals/ColourfulAirBlock.java
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
package quimufu.colourful_portals;
|
||||||
|
|
||||||
|
import net.minecraft.block.*;
|
||||||
|
import net.minecraft.component.DataComponentTypes;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.fluid.Fluid;
|
||||||
|
import net.minecraft.fluid.FluidState;
|
||||||
|
import net.minecraft.item.*;
|
||||||
|
import net.minecraft.item.tooltip.TooltipType;
|
||||||
|
import net.minecraft.network.packet.s2c.play.ParticleS2CPacket;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.state.StateManager;
|
||||||
|
import net.minecraft.state.property.EnumProperty;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
import net.minecraft.util.DyeColor;
|
||||||
|
import net.minecraft.util.Hand;
|
||||||
|
import net.minecraft.util.ItemActionResult;
|
||||||
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
|
import net.minecraft.util.shape.VoxelShapes;
|
||||||
|
import net.minecraft.world.BlockView;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.WorldAccess;
|
||||||
|
import net.minecraft.world.WorldView;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ColourfulAirBlock extends Block implements FluidFillable {
|
||||||
|
public static final EnumProperty<DyeColor> DYE_COLOR = EnumProperty.of("colour", DyeColor.class);
|
||||||
|
|
||||||
|
|
||||||
|
public ColourfulAirBlock(Settings settings) {
|
||||||
|
super(settings);
|
||||||
|
this.setDefaultState(this.stateManager.getDefaultState().with(DYE_COLOR, DyeColor.BLACK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||||
|
if(stack.getItem() == Items.GLASS_BOTTLE){
|
||||||
|
ItemStack filledBottle = getPickStack(world, pos, state);
|
||||||
|
player.setStackInHand(hand, ItemUsage.exchangeStack(stack, player, filledBottle));
|
||||||
|
if(!world.isClient){
|
||||||
|
world.setBlockState(pos, Blocks.AIR.getDefaultState());
|
||||||
|
}
|
||||||
|
return ItemActionResult.success(world.isClient);
|
||||||
|
}
|
||||||
|
return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean notify) {
|
||||||
|
scheduleTick(world, pos, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendTooltip(ItemStack stack,
|
||||||
|
Item.TooltipContext context,
|
||||||
|
List<Text> tooltip, TooltipType options) {
|
||||||
|
|
||||||
|
DyeColor color = stack.getOrDefault(DataComponentTypes.BASE_COLOR, DyeColor.BLACK);
|
||||||
|
tooltip.add(Text.translatable("color.minecraft." + color.getName())
|
||||||
|
.withColor(color.getEntityColor()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getPickStack(WorldView world, BlockPos pos, BlockState state) {
|
||||||
|
ItemStack stack = ColourfulPortalsMod.COLOURFUL_AIR_BOTTLE_ITEM.getDefaultStack();
|
||||||
|
stack.set(DataComponentTypes.BASE_COLOR, state.get(DYE_COLOR));
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos blockPos, ShapeContext shapeContext) {
|
||||||
|
if (shapeContext.isHolding(Items.DEBUG_STICK)
|
||||||
|
|| shapeContext.isHolding(ColourfulPortalsMod.COLOURFUL_AIR_BOTTLE_ITEM)
|
||||||
|
|| shapeContext.isHolding(Items.GLASS_BOTTLE)) {
|
||||||
|
return VoxelShapes.fullCube();
|
||||||
|
}
|
||||||
|
return VoxelShapes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BlockRenderType getRenderType(BlockState state) {
|
||||||
|
return BlockRenderType.INVISIBLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||||
|
scheduleTick(world, pos, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
|
||||||
|
DyeColor dyeColor = state.get(DYE_COLOR);
|
||||||
|
for (ServerPlayerEntity player : world.getPlayers()) {
|
||||||
|
double x = pos.getX() + .5D;
|
||||||
|
double y = pos.getY() + .5D;
|
||||||
|
double z = pos.getZ() + .5D;
|
||||||
|
ParticleS2CPacket particleS2CPacket = new ParticleS2CPacket(
|
||||||
|
ColourfulPortalsMod.COLOURFUL_AIR_PARTICLE_BY_COLOUR.get(dyeColor),
|
||||||
|
true,
|
||||||
|
x, y, z,
|
||||||
|
0.25F, 0.25F, 0.25F,
|
||||||
|
0,
|
||||||
|
1);
|
||||||
|
|
||||||
|
world.sendToPlayerIfNearby(player, true, x, y, z, particleS2CPacket);
|
||||||
|
}
|
||||||
|
scheduleTick(world, pos, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleTick(WorldAccess world, BlockPos pos, boolean asap) {
|
||||||
|
if (!world.isClient() && !world.getBlockTickScheduler().isQueued(pos, this)) {
|
||||||
|
if(asap){
|
||||||
|
world.scheduleBlockTick(pos, this, 2);
|
||||||
|
} else if (world.getClosestPlayer(pos.getX(),pos.getY(),pos.getZ(),512,false) != null) {
|
||||||
|
world.scheduleBlockTick(pos, this, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||||
|
DyeColor color = ctx.getStack().getOrDefault(DataComponentTypes.BASE_COLOR, DyeColor.BLACK);
|
||||||
|
return this.getDefaultState().with(DYE_COLOR, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockState getRandomState(org.joml.Random random) {
|
||||||
|
DyeColor color = DyeColor.values()[random.nextInt(DyeColor.values().length)];
|
||||||
|
return this.getDefaultState().with(DYE_COLOR, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
|
builder.add(DYE_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canFillWithFluid(@Nullable PlayerEntity player, BlockView world, BlockPos pos, BlockState state, Fluid fluid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean tryFillWithFluid(WorldAccess world, BlockPos pos, BlockState state, FluidState fluidState) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,30 @@
|
|||||||
package quimufu.colourful_portals;
|
package quimufu.colourful_portals;
|
||||||
|
|
||||||
|
import eu.midnightdust.lib.config.MidnightConfig;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
|
import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder;
|
||||||
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroupEntries;
|
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroupEntries;
|
||||||
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
|
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
|
||||||
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
|
import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes;
|
||||||
|
import net.minecraft.block.AbstractBlock;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.enums.Instrument;
|
import net.minecraft.block.enums.NoteBlockInstrument;
|
||||||
|
import net.minecraft.component.DataComponentTypes;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
|
import net.minecraft.entity.SpawnGroup;
|
||||||
import net.minecraft.item.*;
|
import net.minecraft.item.*;
|
||||||
|
import net.minecraft.particle.SimpleParticleType;
|
||||||
import net.minecraft.registry.Registries;
|
import net.minecraft.registry.Registries;
|
||||||
import net.minecraft.registry.Registry;
|
import net.minecraft.registry.Registry;
|
||||||
|
import net.minecraft.registry.RegistryKey;
|
||||||
|
import net.minecraft.registry.RegistryKeys;
|
||||||
|
import net.minecraft.registry.tag.TagKey;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.sound.BlockSoundGroup;
|
import net.minecraft.sound.BlockSoundGroup;
|
||||||
|
import net.minecraft.sound.SoundEvent;
|
||||||
|
import net.minecraft.util.DyeColor;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.Rarity;
|
import net.minecraft.util.Rarity;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
@@ -19,25 +32,62 @@ import net.minecraft.world.BlockView;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import quimufu.colourful_portals.config.ColourfulPortalConfig;
|
import quimufu.colourful_portals.config.ColourfulPortalConfig;
|
||||||
|
import quimufu.colourful_portals.entity.ColourfulPearlEntity;
|
||||||
|
import quimufu.colourful_portals.general_util.WeightedSelector;
|
||||||
|
import quimufu.colourful_portals.item.ColourfulAirBottleItem;
|
||||||
|
import quimufu.colourful_portals.item.ColourfulPearlItem;
|
||||||
|
import quimufu.colourful_portals.portal.*;
|
||||||
import quimufu.colourful_portals.portal_fluid.PortalFluid;
|
import quimufu.colourful_portals.portal_fluid.PortalFluid;
|
||||||
import quimufu.colourful_portals.portal_fluid.PortalFluidBlock;
|
import quimufu.colourful_portals.portal_fluid.PortalFluidBlock;
|
||||||
import quimufu.colourful_portals.portal_fluid.PortalFluidBucketItem;
|
import quimufu.colourful_portals.portal_fluid.PortalFluidBucketItem;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.Components.PORTAL_CANDIDATE_LIST;
|
||||||
|
import static quimufu.colourful_portals.Components.PORTAL_LIST;
|
||||||
|
|
||||||
public class ColourfulPortalsMod implements ModInitializer {
|
public class ColourfulPortalsMod implements ModInitializer {
|
||||||
|
|
||||||
public static final String MOD_ID = "colourful_portals";
|
public static final String MOD_ID = "colourful_portals";
|
||||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||||
|
|
||||||
public static final HashSet<Identifier> PORTAL_BLOCKS = new HashSet<>();
|
public static final HashSet<Identifier> PORTAL_BLOCKS = new HashSet<>();
|
||||||
|
|
||||||
public static final PortalBlock PORTAL_BLOCK = new PortalBlock(FabricBlockSettings.create().instrument(Instrument.HAT).sounds(BlockSoundGroup.GLASS).strength(-1.0f, 3600000.8f).dropsNothing().nonOpaque().luminance(15).allowsSpawning(ColourfulPortalsMod::never).solidBlock(ColourfulPortalsMod::never).suffocates(ColourfulPortalsMod::never).blockVision(ColourfulPortalsMod::never).ticksRandomly());
|
public static final ColourfulAirBlock COLOURFUL_AIR = new ColourfulAirBlock(AbstractBlock.Settings.create().instrument(NoteBlockInstrument.HAT).sounds(BlockSoundGroup.GLASS).strength(-1.0f, 3600000.8f).dropsNothing().noCollision().luminance((bs) -> 7).allowsSpawning(ColourfulPortalsMod::never).solidBlock(ColourfulPortalsMod::never).suffocates(ColourfulPortalsMod::never).blockVision(ColourfulPortalsMod::never).ticksRandomly());
|
||||||
public static final BlockItem PORTAL_BLOCK_ITEM = new BlockItem(PORTAL_BLOCK, new FabricItemSettings().rarity(Rarity.EPIC));
|
public static final ColourfulAirBottleItem COLOURFUL_AIR_BOTTLE_ITEM = new ColourfulAirBottleItem(COLOURFUL_AIR, new Item.Settings().rarity(Rarity.RARE).component(DataComponentTypes.BASE_COLOR, DyeColor.BLACK));
|
||||||
public static final Item BLOB_DARK = new Item(new FabricItemSettings());
|
|
||||||
public static final Item BLOB_BRIGHT = new Item(new FabricItemSettings());
|
public static final EnumMap<DyeColor, SimpleParticleType> COLOURFUL_AIR_PARTICLE_BY_COLOUR;
|
||||||
|
|
||||||
|
static {
|
||||||
|
COLOURFUL_AIR_PARTICLE_BY_COLOUR = new EnumMap<>(DyeColor.class);
|
||||||
|
for (DyeColor dyeColor : DyeColor.values()) {
|
||||||
|
COLOURFUL_AIR_PARTICLE_BY_COLOUR.put(dyeColor, FabricParticleTypes.simple(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final PortalBlock PORTAL_BLOCK = new PortalBlock(AbstractBlock.Settings.create().instrument(NoteBlockInstrument.HAT).sounds(BlockSoundGroup.GLASS).strength(-1.0f, 3600000.8f).dropsNothing().noCollision().luminance((bs) -> 15).allowsSpawning(ColourfulPortalsMod::never).solidBlock(ColourfulPortalsMod::never).suffocates(ColourfulPortalsMod::never).blockVision(ColourfulPortalsMod::never).ticksRandomly());
|
||||||
|
public static final BlockItem PORTAL_BLOCK_ITEM = new BlockItem(PORTAL_BLOCK, new Item.Settings().rarity(Rarity.EPIC));
|
||||||
|
|
||||||
|
public static final Item BLOB_DARK = new Item(new Item.Settings());
|
||||||
|
public static final Item BLOB_BRIGHT = new Item(new Item.Settings());
|
||||||
|
|
||||||
|
public static final Identifier COLOURFUL_PEARL_ID = Identifier.of(MOD_ID, "colourful_pearl");
|
||||||
|
public static final Item COLOURFUL_PEARL_ITEM = new ColourfulPearlItem(new Item.Settings().maxCount(8));
|
||||||
|
public static final TagKey<Block> COLOURFUL_PEARL_REPLACEABLE_BLOCK_TAG = TagKey.of(RegistryKeys.BLOCK, Identifier.of(MOD_ID, "colourful_pearl_replaceable"));
|
||||||
|
public static final WeightedSelector<Identifier> DIMENSION_WEIGHTS_COLOURFUL_PEARL = new WeightedSelector<>();
|
||||||
|
public static final EntityType<ColourfulPearlEntity> COLOURFUL_PEARL_ENTITY_TYPE = EntityType.Builder.<ColourfulPearlEntity>create(ColourfulPearlEntity::new, SpawnGroup.MISC).dimensions(0.25f, 0.25f).maxTrackingRange(4).trackingTickInterval(10).build(COLOURFUL_PEARL_ID.toString());
|
||||||
|
public static final TagKey<EntityType<?>> COLOURFUL_PEARL_NOT_TELEPORTABLE = TagKey.of(RegistryKeys.ENTITY_TYPE, Identifier.of(MOD_ID, "pearl_not_teleportable"));
|
||||||
|
|
||||||
|
public static final SoundEvent TELEPORT_AWAY_SOUND = SoundEvent.of(Identifier.of(MOD_ID, "entity.colourful_pearl.teleport_away"));
|
||||||
|
|
||||||
|
|
||||||
public static final PortalFluid PORTAL_FLUID = new PortalFluid();
|
public static final PortalFluid PORTAL_FLUID = new PortalFluid();
|
||||||
public static final PortalFluidBlock PORTAL_FLUID_BLOCk = new PortalFluidBlock(PORTAL_FLUID, FabricBlockSettings.create().sounds(BlockSoundGroup.field_44608).luminance(15).noCollision().strength(100.0f).dropsNothing());
|
public static final PortalFluidBlock PORTAL_FLUID_BLOCk = new PortalFluidBlock(PORTAL_FLUID, AbstractBlock.Settings.create().sounds(BlockSoundGroup.INTENTIONALLY_EMPTY).luminance((bs) -> 15).noCollision().strength(100.0f).dropsNothing());
|
||||||
public static final BucketItem PORTAL_FLUID_BUCKET_ITEM = new PortalFluidBucketItem(PORTAL_FLUID, new FabricItemSettings().recipeRemainder(Items.BUCKET).maxCount(1).rarity(Rarity.RARE));
|
public static final BucketItem PORTAL_FLUID_BUCKET_ITEM = new PortalFluidBucketItem(PORTAL_FLUID, new Item.Settings().recipeRemainder(Items.BUCKET).maxCount(1).rarity(Rarity.RARE));
|
||||||
|
|
||||||
|
public static PortalManager PORTAL_MANAGER;
|
||||||
|
public static final RegistryKey<Registry<PrioritizedPortalLinkingSystemBuilder>> PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY_KEY = RegistryKey.ofRegistry(Identifier.of(MOD_ID, "portal_linking_system"));
|
||||||
|
public static final Registry<PrioritizedPortalLinkingSystemBuilder> PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY = FabricRegistryBuilder.createSimple(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY_KEY).buildAndRegister();
|
||||||
|
|
||||||
private static boolean never(BlockState blockState, BlockView blockView, BlockPos blockPos, EntityType<?> entityType) {
|
private static boolean never(BlockState blockState, BlockView blockView, BlockPos blockPos, EntityType<?> entityType) {
|
||||||
return false;
|
return false;
|
||||||
@@ -50,23 +100,39 @@ public class ColourfulPortalsMod implements ModInitializer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
LOGGER.info("Colourizing Portals...");
|
LOGGER.info("Colouring Portals...");
|
||||||
|
MidnightConfig.init(MOD_ID, ColourfulPortalConfig.class);
|
||||||
|
ColourfulPortalConfig.registerListener(this::onConfigUpdate);
|
||||||
|
|
||||||
for (String id : ColourfulPortalConfig.portalBlocks.keySet()) {
|
onConfigUpdate();
|
||||||
PORTAL_BLOCKS.add(Identifier.tryParse(id));
|
if (hasImmPtl()) {
|
||||||
|
Registry.register(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY, ImmersivePortalsLinkingSystem.IMMERSIVE_PORTALS_LINKING_SYSTEM, new PrioritizedPortalLinkingSystemBuilder(ImmersivePortalsLinkingSystem::new, () -> ColourfulPortalConfig.disableImmersivePortals ? -1 : 100));
|
||||||
|
}
|
||||||
|
for (Map.Entry<DyeColor, SimpleParticleType> entry : COLOURFUL_AIR_PARTICLE_BY_COLOUR.entrySet()) {
|
||||||
|
Registry.register(Registries.PARTICLE_TYPE, Identifier.of(MOD_ID, entry.getKey().getName() + "_colourful_air_sparkle"), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
Identifier identifier = new Identifier(MOD_ID, "portal_block");
|
Registry.register(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY, DefaultLinkingSystem.DEFAULT_LINKING_SYSTEM, new PrioritizedPortalLinkingSystemBuilder(DefaultLinkingSystem::new, () -> 90));
|
||||||
|
|
||||||
|
Identifier identifier = Identifier.of(MOD_ID, "portal_block");
|
||||||
|
|
||||||
Registry.register(Registries.BLOCK, identifier, PORTAL_BLOCK);
|
Registry.register(Registries.BLOCK, identifier, PORTAL_BLOCK);
|
||||||
Registry.register(Registries.ITEM, identifier, PORTAL_BLOCK_ITEM);
|
Registry.register(Registries.ITEM, identifier, PORTAL_BLOCK_ITEM);
|
||||||
|
|
||||||
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "portal_fluid_bucket"), PORTAL_FLUID_BUCKET_ITEM);
|
Registry.register(Registries.BLOCK, Identifier.of(MOD_ID, "colourful_air"), COLOURFUL_AIR);
|
||||||
Registry.register(Registries.FLUID, new Identifier(MOD_ID, "portal_fluid"), PORTAL_FLUID);
|
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "colourful_air_bottle"), COLOURFUL_AIR_BOTTLE_ITEM);
|
||||||
Registry.register(Registries.BLOCK, new Identifier(MOD_ID, "portal_fluid_block"), PORTAL_FLUID_BLOCk);
|
|
||||||
|
|
||||||
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "colour_blob_bright"), BLOB_BRIGHT);
|
|
||||||
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "colour_blob_dark"), BLOB_DARK);
|
Registry.register(Registries.ENTITY_TYPE, COLOURFUL_PEARL_ID, COLOURFUL_PEARL_ENTITY_TYPE);
|
||||||
|
Registry.register(Registries.ITEM, COLOURFUL_PEARL_ID, COLOURFUL_PEARL_ITEM);
|
||||||
|
Registry.register(Registries.SOUND_EVENT, TELEPORT_AWAY_SOUND.getId(), TELEPORT_AWAY_SOUND);
|
||||||
|
|
||||||
|
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "portal_fluid_bucket"), PORTAL_FLUID_BUCKET_ITEM);
|
||||||
|
Registry.register(Registries.FLUID, Identifier.of(MOD_ID, "portal_fluid"), PORTAL_FLUID);
|
||||||
|
Registry.register(Registries.BLOCK, Identifier.of(MOD_ID, "portal_fluid_block"), PORTAL_FLUID_BLOCk);
|
||||||
|
|
||||||
|
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "colour_blob_bright"), BLOB_BRIGHT);
|
||||||
|
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "colour_blob_dark"), BLOB_DARK);
|
||||||
|
|
||||||
ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS)
|
ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS)
|
||||||
.register(ColourfulPortalsMod::addToIngredients);
|
.register(ColourfulPortalsMod::addToIngredients);
|
||||||
@@ -74,9 +140,65 @@ public class ColourfulPortalsMod implements ModInitializer {
|
|||||||
ItemGroupEvents.modifyEntriesEvent(ItemGroups.TOOLS)
|
ItemGroupEvents.modifyEntriesEvent(ItemGroups.TOOLS)
|
||||||
.register(ColourfulPortalsMod::addTtTools);
|
.register(ColourfulPortalsMod::addTtTools);
|
||||||
|
|
||||||
|
ServerLifecycleEvents.SERVER_STARTED.register(this::onServerStarted);
|
||||||
LOGGER.info("Portals Colourful!");
|
LOGGER.info("Portals Colourful!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onConfigUpdate() {
|
||||||
|
PORTAL_BLOCKS.clear();
|
||||||
|
for (String id : ColourfulPortalConfig.getAllPortalBlocks()) {
|
||||||
|
LOGGER.info(id);
|
||||||
|
PORTAL_BLOCKS.add(Identifier.tryParse(id));
|
||||||
|
}
|
||||||
|
DIMENSION_WEIGHTS_COLOURFUL_PEARL.clear();
|
||||||
|
for (Map.Entry<String, Integer> weight : ColourfulPortalConfig.pearlDimensionWeights.entrySet()) {
|
||||||
|
DIMENSION_WEIGHTS_COLOURFUL_PEARL.add(Identifier.of(weight.getKey()), weight.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasImmPtl() {
|
||||||
|
try {
|
||||||
|
Class.forName("qouteall.imm_ptl.core.IPModMain", false, this.getClass().getClassLoader());
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onServerStarted(MinecraftServer minecraftServer) {
|
||||||
|
|
||||||
|
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(minecraftServer.getSaveProperties().getMainWorldProperties());
|
||||||
|
PortalListComponent portalList = PORTAL_LIST.get(minecraftServer.getSaveProperties().getMainWorldProperties());
|
||||||
|
|
||||||
|
PortalLinkingSystem currentLinkingSystem = getCurrentlyConfiguredLinkingSystem(minecraftServer);
|
||||||
|
if (!Objects.equals(currentLinkingSystem.getLinkingSystemId(), portalList.lastPortalLinkingSystemId()) || currentLinkingSystem.needsReInit()) {
|
||||||
|
PrioritizedPortalLinkingSystemBuilder prevSystem;
|
||||||
|
if ((prevSystem = PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY.get(portalList.lastPortalLinkingSystemId())) != null) {
|
||||||
|
PortalLinkingSystem prevLS = prevSystem.portalLinkingSystemBuilder().build(minecraftServer);
|
||||||
|
for (Identifier blockId : portalList.getBlockIds()) {
|
||||||
|
for (PortalRepresentation portal : portalList.getPortals(blockId)) {
|
||||||
|
prevLS.unLinkPortal(portal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Identifier blockId : portalList.getBlockIds()) {
|
||||||
|
currentLinkingSystem.linkPortals(portalList.getPortals(blockId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PORTAL_MANAGER = new PortalManager(currentLinkingSystem, portalCandidateList, portalList);
|
||||||
|
PORTAL_MANAGER.onLoad(minecraftServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PortalLinkingSystem getCurrentlyConfiguredLinkingSystem(MinecraftServer minecraftServer) {
|
||||||
|
return PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY
|
||||||
|
.stream()
|
||||||
|
.filter(prio -> prio.priority().getAsInt() >= 0)
|
||||||
|
.max(Comparator.comparing(prio -> prio.priority().getAsInt()))
|
||||||
|
.map(PrioritizedPortalLinkingSystemBuilder::portalLinkingSystemBuilder)
|
||||||
|
.orElse(DefaultLinkingSystem::new)
|
||||||
|
.build(minecraftServer);
|
||||||
|
}
|
||||||
|
|
||||||
private static void addTtTools(FabricItemGroupEntries entries) {
|
private static void addTtTools(FabricItemGroupEntries entries) {
|
||||||
entries.add(PORTAL_FLUID_BUCKET_ITEM);
|
entries.add(PORTAL_FLUID_BUCKET_ITEM);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package quimufu.colourful_portals;
|
|
||||||
|
|
||||||
import net.fabricmc.api.ClientModInitializer;
|
|
||||||
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
|
||||||
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
|
|
||||||
import net.minecraft.client.render.RenderLayer;
|
|
||||||
import quimufu.colourful_portals.client.PortalFluidRenderHandler;
|
|
||||||
|
|
||||||
public class ColourfulPortalsModClient implements ClientModInitializer {
|
|
||||||
public void onInitializeClient() {
|
|
||||||
BlockRenderLayerMap.INSTANCE.putBlock(ColourfulPortalsMod.PORTAL_BLOCK, RenderLayer.getTranslucent());
|
|
||||||
BlockRenderLayerMap.INSTANCE.putFluid(ColourfulPortalsMod.PORTAL_FLUID, RenderLayer.getTranslucent());
|
|
||||||
FluidRenderHandlerRegistry.INSTANCE.register(ColourfulPortalsMod.PORTAL_FLUID, new PortalFluidRenderHandler());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,22 @@
|
|||||||
package quimufu.colourful_portals;
|
package quimufu.colourful_portals;
|
||||||
|
|
||||||
import dev.onyxstudios.cca.api.v3.component.ComponentKey;
|
import org.ladysnake.cca.api.v3.component.ComponentKey;
|
||||||
import dev.onyxstudios.cca.api.v3.component.ComponentRegistry;
|
import org.ladysnake.cca.api.v3.component.ComponentRegistry;
|
||||||
import dev.onyxstudios.cca.api.v3.level.LevelComponentFactoryRegistry;
|
import org.ladysnake.cca.api.v3.level.LevelComponentFactoryRegistry;
|
||||||
import dev.onyxstudios.cca.api.v3.level.LevelComponentInitializer;
|
import org.ladysnake.cca.api.v3.level.LevelComponentInitializer;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import quimufu.colourful_portals.portal.PortalListComponent;
|
import quimufu.colourful_portals.portal.PortalListComponent;
|
||||||
|
|
||||||
public class Components implements LevelComponentInitializer {
|
public class Components implements LevelComponentInitializer {
|
||||||
public static final ComponentKey<PortalListComponent> PORTAL_LIST =
|
public static final ComponentKey<PortalListComponent> PORTAL_LIST =
|
||||||
ComponentRegistry.getOrCreate(new Identifier(ColourfulPortalsMod.MOD_ID, "portal_list"), PortalListComponent.class);
|
ComponentRegistry.getOrCreate(Identifier.of(ColourfulPortalsMod.MOD_ID, "portal_list"), PortalListComponent.class);
|
||||||
public static final ComponentKey<PortalListComponent> PORTAL_CANDIDATE_LIST =
|
public static final ComponentKey<PortalListComponent> PORTAL_CANDIDATE_LIST =
|
||||||
ComponentRegistry.getOrCreate(new Identifier(ColourfulPortalsMod.MOD_ID, "portal_candidate_list"), PortalListComponent.class);
|
ComponentRegistry.getOrCreate(Identifier.of(ColourfulPortalsMod.MOD_ID, "portal_candidate_list"), PortalListComponent.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerLevelComponentFactories(LevelComponentFactoryRegistry registry) {
|
public void registerLevelComponentFactories(LevelComponentFactoryRegistry registry) {
|
||||||
registry.register(PORTAL_LIST, PortalListComponent::new);
|
registry.register(PORTAL_LIST, worldProperties1 -> new PortalListComponent());
|
||||||
registry.register(PORTAL_CANDIDATE_LIST, PortalListComponent::new);
|
registry.register(PORTAL_CANDIDATE_LIST, worldProperties -> new PortalListComponent());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class MixinConfig implements IMixinConfigPlugin {
|
|||||||
private static final Supplier<Boolean> TRUE = () -> true;
|
private static final Supplier<Boolean> TRUE = () -> true;
|
||||||
|
|
||||||
private static final Map<String, Supplier<Boolean>> CONDITIONS = Map.of(
|
private static final Map<String, Supplier<Boolean>> CONDITIONS = Map.of(
|
||||||
"quimufu.colourful_portals.mixin.SodiumFluidRendererMixin", () -> FabricLoader.getInstance().isModLoaded("sodium")
|
"quimufu.colourful_portals.client.mixin.SodiumFluidRendererMixin", () -> FabricLoader.getInstance().isModLoaded("sodium")
|
||||||
);
|
);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import net.minecraft.block.Block;
|
|||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.FluidFillable;
|
import net.minecraft.block.FluidFillable;
|
||||||
import net.minecraft.block.ShapeContext;
|
import net.minecraft.block.ShapeContext;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.fluid.Fluid;
|
import net.minecraft.fluid.Fluid;
|
||||||
import net.minecraft.fluid.FluidState;
|
import net.minecraft.fluid.FluidState;
|
||||||
import net.minecraft.item.ItemPlacementContext;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
@@ -14,14 +15,15 @@ import net.minecraft.state.property.EnumProperty;
|
|||||||
import net.minecraft.state.property.Properties;
|
import net.minecraft.state.property.Properties;
|
||||||
import net.minecraft.util.BlockRotation;
|
import net.minecraft.util.BlockRotation;
|
||||||
import net.minecraft.util.DyeColor;
|
import net.minecraft.util.DyeColor;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.*;
|
||||||
import net.minecraft.util.math.Direction;
|
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
import net.minecraft.util.shape.VoxelShapes;
|
import net.minecraft.util.shape.VoxelShapes;
|
||||||
import net.minecraft.world.BlockView;
|
import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.WorldAccess;
|
||||||
import quimufu.colourful_portals.portal.PortalManager;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_MANAGER;
|
||||||
|
|
||||||
|
|
||||||
public class PortalBlock extends Block implements FluidFillable {
|
public class PortalBlock extends Block implements FluidFillable {
|
||||||
@@ -63,19 +65,18 @@ public class PortalBlock extends Block implements FluidFillable {
|
|||||||
if (shapeContext.isHolding(Items.DEBUG_STICK)
|
if (shapeContext.isHolding(Items.DEBUG_STICK)
|
||||||
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_BLOCK_ITEM)
|
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_BLOCK_ITEM)
|
||||||
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_FLUID_BUCKET_ITEM)) {
|
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_FLUID_BUCKET_ITEM)) {
|
||||||
|
return getShape(state);
|
||||||
|
}
|
||||||
|
return VoxelShapes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public VoxelShape getShape(BlockState state) {
|
||||||
return switch (state.get(AXIS)) {
|
return switch (state.get(AXIS)) {
|
||||||
case Z -> Z_AABB;
|
case Z -> Z_AABB;
|
||||||
case Y -> Y_AABB;
|
case Y -> Y_AABB;
|
||||||
default -> X_AABB;
|
default -> X_AABB;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return VoxelShapes.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
|
||||||
return VoxelShapes.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockState rotate(BlockState state, BlockRotation rotation) {
|
public BlockState rotate(BlockState state, BlockRotation rotation) {
|
||||||
@@ -100,7 +101,6 @@ public class PortalBlock extends Block implements FluidFillable {
|
|||||||
|
|
||||||
public BlockState getStateWith(DyeColor color, Direction.Axis axis) {
|
public BlockState getStateWith(DyeColor color, Direction.Axis axis) {
|
||||||
return this.getDefaultState().with(AXIS, axis).with(DYE_COLOR, color);
|
return this.getDefaultState().with(AXIS, axis).with(DYE_COLOR, color);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -119,15 +119,16 @@ public class PortalBlock extends Block implements FluidFillable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canFillWithFluid(BlockView world, BlockPos pos, BlockState state, Fluid fluid) {
|
public boolean canFillWithFluid(@Nullable PlayerEntity player, BlockView world, BlockPos pos, BlockState state, Fluid fluid) {
|
||||||
if (((World) world).isClient() && fluid == ColourfulPortalsMod.PORTAL_FLUID) {
|
if (((World) world).isClient() && fluid == ColourfulPortalsMod.PORTAL_FLUID) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return fluid == ColourfulPortalsMod.PORTAL_FLUID
|
return fluid == ColourfulPortalsMod.PORTAL_FLUID
|
||||||
&& (world instanceof ServerWorld)
|
&& (world instanceof ServerWorld)
|
||||||
&& PortalManager.canExtend((ServerWorld) world, pos);
|
&& PORTAL_MANAGER.canExtend((ServerWorld) world, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean tryFillWithFluid(WorldAccess world, BlockPos pos, BlockState state, FluidState fluidState) {
|
public boolean tryFillWithFluid(WorldAccess world, BlockPos pos, BlockState state, FluidState fluidState) {
|
||||||
if (world.isClient() && fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)) {
|
if (world.isClient() && fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)) {
|
||||||
@@ -135,6 +136,6 @@ public class PortalBlock extends Block implements FluidFillable {
|
|||||||
}
|
}
|
||||||
return fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)
|
return fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)
|
||||||
&& (world instanceof ServerWorld)
|
&& (world instanceof ServerWorld)
|
||||||
&& PortalManager.extend((ServerWorld) world, pos);
|
&& PORTAL_MANAGER.extend((ServerWorld) world, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package quimufu.colourful_portals.client;
|
||||||
|
|
||||||
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap;
|
||||||
|
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry;
|
||||||
|
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
|
||||||
|
import net.minecraft.client.item.ModelPredicateProviderRegistry;
|
||||||
|
import net.minecraft.client.render.RenderLayer;
|
||||||
|
import net.minecraft.client.render.entity.FlyingItemEntityRenderer;
|
||||||
|
import net.minecraft.component.DataComponentTypes;
|
||||||
|
import net.minecraft.particle.SimpleParticleType;
|
||||||
|
import net.minecraft.util.DyeColor;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
|
import quimufu.colourful_portals.client.particle.ColourfulAirSparkleParticle;
|
||||||
|
import quimufu.colourful_portals.client.rendering.fluid.CommonPortalFluidRenderer;
|
||||||
|
import quimufu.colourful_portals.client.rendering.fluid.CommonPortalFluidRendererV1;
|
||||||
|
import quimufu.colourful_portals.client.rendering.fluid.CommonPortalFluidRendererV2;
|
||||||
|
import quimufu.colourful_portals.client.rendering.fluid.PortalFluidRenderHandler;
|
||||||
|
import quimufu.colourful_portals.config.ColourfulPortalConfig;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ColourfulPortalsModClient implements ClientModInitializer {
|
||||||
|
public static ThreadLocal<CommonPortalFluidRenderer> FLUID_RENDERER = ThreadLocal.withInitial(CommonPortalFluidRendererV2::new);
|
||||||
|
|
||||||
|
public void onInitializeClient() {
|
||||||
|
BlockRenderLayerMap.INSTANCE.putBlock(ColourfulPortalsMod.PORTAL_BLOCK, RenderLayer.getTranslucent());
|
||||||
|
BlockRenderLayerMap.INSTANCE.putBlock(ColourfulPortalsMod.COLOURFUL_AIR, RenderLayer.getTranslucent());
|
||||||
|
BlockRenderLayerMap.INSTANCE.putFluid(ColourfulPortalsMod.PORTAL_FLUID, RenderLayer.getTranslucent());
|
||||||
|
FluidRenderHandlerRegistry.INSTANCE.register(ColourfulPortalsMod.PORTAL_FLUID, new PortalFluidRenderHandler());
|
||||||
|
|
||||||
|
EntityRendererRegistry.register(ColourfulPortalsMod.COLOURFUL_PEARL_ENTITY_TYPE, FlyingItemEntityRenderer::new);
|
||||||
|
|
||||||
|
ColourfulPortalConfig.registerListener(this::onConfigUpdate);
|
||||||
|
onConfigUpdate();
|
||||||
|
|
||||||
|
|
||||||
|
for (Map.Entry<DyeColor, SimpleParticleType> entry : ColourfulPortalsMod.COLOURFUL_AIR_PARTICLE_BY_COLOUR.entrySet()) {
|
||||||
|
ParticleFactoryRegistry.getInstance()
|
||||||
|
.register(entry.getValue(), ColourfulAirSparkleParticle::create);
|
||||||
|
}
|
||||||
|
ModelPredicateProviderRegistry.register(ColourfulPortalsMod.COLOURFUL_AIR_BOTTLE_ITEM, Identifier.of(ColourfulPortalsMod.MOD_ID,"color_id"),((stack, world, entity, seed) -> stack.getOrDefault(DataComponentTypes.BASE_COLOR, DyeColor.BLACK).getId()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onConfigUpdate() {
|
||||||
|
if (ColourfulPortalConfig.blockyPortalFluid) {
|
||||||
|
FLUID_RENDERER = ThreadLocal.withInitial(CommonPortalFluidRendererV1::new);
|
||||||
|
} else {
|
||||||
|
FLUID_RENDERER = ThreadLocal.withInitial(CommonPortalFluidRendererV2::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
package quimufu.colourful_portals.client;
|
|
||||||
|
|
||||||
import me.jellysquid.mods.sodium.client.model.quad.ModelQuad;
|
|
||||||
import me.jellysquid.mods.sodium.client.model.quad.ModelQuadView;
|
|
||||||
import me.jellysquid.mods.sodium.client.model.quad.ModelQuadViewMutable;
|
|
||||||
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing;
|
|
||||||
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadWinding;
|
|
||||||
import me.jellysquid.mods.sodium.client.render.chunk.compile.buffers.ChunkModelBuilder;
|
|
||||||
import me.jellysquid.mods.sodium.client.render.vertex.type.ChunkVertexEncoder;
|
|
||||||
import net.fabricmc.api.EnvType;
|
|
||||||
import net.fabricmc.api.Environment;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
|
||||||
import net.minecraft.fluid.FluidState;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.Direction;
|
|
||||||
import net.minecraft.world.BlockRenderView;
|
|
||||||
|
|
||||||
@Environment(value = EnvType.CLIENT)
|
|
||||||
public class SodiumPortalFluidRenderHandler implements CommonPortalFluidRenderer.VertexEater {
|
|
||||||
|
|
||||||
private Sprite sprite = null;
|
|
||||||
private final ModelQuadViewMutable quad = new ModelQuad();
|
|
||||||
|
|
||||||
private final CommonPortalFluidRenderer commonPortalFluidRenderer = new CommonPortalFluidRenderer();
|
|
||||||
private final ChunkVertexEncoder.Vertex[] vertices = ChunkVertexEncoder.Vertex.uninitializedQuad();
|
|
||||||
private int i;
|
|
||||||
private ChunkModelBuilder chunkModelBuilder;
|
|
||||||
|
|
||||||
public SodiumPortalFluidRenderHandler() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean render(BlockRenderView world, FluidState fluidState, BlockPos pos, BlockPos offset, ChunkModelBuilder buffers) {
|
|
||||||
this.chunkModelBuilder = buffers;
|
|
||||||
return commonPortalFluidRenderer.render(world,fluidState,pos,offset,this);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setSprite(Sprite sprite) {
|
|
||||||
quad.setSprite(sprite);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eatVertex(float x, float y, float z, float u, float v) {
|
|
||||||
quad.setX(i, x);
|
|
||||||
quad.setY(i, y);
|
|
||||||
quad.setZ(i, z);
|
|
||||||
quad.setTexU(i, u);
|
|
||||||
quad.setTexV(i, v);
|
|
||||||
i = (i+1)%4;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawQuad(BlockPos offset, Direction direction) {
|
|
||||||
writeQuad(chunkModelBuilder, offset, quad, ModelQuadFacing.fromDirection(direction), ModelQuadWinding.CLOCKWISE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeQuad(ChunkModelBuilder builder, BlockPos offset, ModelQuadView quad, ModelQuadFacing facing, ModelQuadWinding winding) {
|
|
||||||
var vertexBuffer = builder.getVertexBuffer();
|
|
||||||
var vertices = this.vertices;
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
var out = vertices[i];
|
|
||||||
out.x = offset.getX() + quad.getX(i);
|
|
||||||
out.y = offset.getY() + quad.getY(i);
|
|
||||||
out.z = offset.getZ() + quad.getZ(i);
|
|
||||||
out.color = 0xFFFFFFFF;
|
|
||||||
out.u = quad.getTexU(i);
|
|
||||||
out.v = quad.getTexV(i);
|
|
||||||
out.light = 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
Sprite sprite = quad.getSprite();
|
|
||||||
|
|
||||||
if (sprite != null) {
|
|
||||||
builder.addSprite(sprite);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.getIndexBuffer(facing)
|
|
||||||
.add(vertexBuffer.push(vertices), winding);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package quimufu.colourful_portals.client.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.texture.Animator;
|
||||||
|
import net.minecraft.client.texture.SpriteContents;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import quimufu.colourful_portals.client.rendering.AlphaBlendingAnimator;
|
||||||
|
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mixin(SpriteContents.Animation.class)
|
||||||
|
public class AnimationMixin implements AlphaInterpolationHolder {
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private boolean interpolation;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private SpriteContents parent;
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private boolean interpolateAlpha = false;
|
||||||
|
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
public void assignParent(SpriteContents parent, List frames, int frameCount, boolean interpolation, CallbackInfo ci) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void colourful_portals$setInterpolateAlpha(boolean interpolateAlpha) {
|
||||||
|
this.interpolateAlpha = interpolateAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean colourful_portals$isInterpolateAlpha() {
|
||||||
|
return interpolateAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "createAnimator()Lnet/minecraft/client/texture/Animator;", cancellable = true)
|
||||||
|
public void createAnimator(CallbackInfoReturnable<Animator> cir) {
|
||||||
|
if (this.interpolateAlpha && this.interpolation) {
|
||||||
|
cir.setReturnValue(new AlphaBlendingAnimator(parent, (SpriteContents.Animation) (Object) this));
|
||||||
|
cir.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package quimufu.colourful_portals.client.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.resource.metadata.AnimationResourceMetadata;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
|
||||||
|
|
||||||
|
@Mixin(AnimationResourceMetadata.class)
|
||||||
|
public class AnimationResourceMetadataMixin implements AlphaInterpolationHolder {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private boolean interpolateAlpha = false;
|
||||||
|
public void colourful_portals$setInterpolateAlpha(boolean interpolateAlpha) {
|
||||||
|
this.interpolateAlpha = interpolateAlpha;
|
||||||
|
}
|
||||||
|
public boolean colourful_portals$isInterpolateAlpha() {
|
||||||
|
return interpolateAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package quimufu.colourful_portals.client.mixin;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import net.minecraft.client.resource.metadata.AnimationResourceMetadata;
|
||||||
|
import net.minecraft.client.resource.metadata.AnimationResourceMetadataReader;
|
||||||
|
import net.minecraft.util.JsonHelper;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
|
||||||
|
|
||||||
|
@Mixin(AnimationResourceMetadataReader.class)
|
||||||
|
public class AnimationResourceMetadataReaderMixin {
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "fromJson(Lcom/google/gson/JsonObject;)Lnet/minecraft/client/resource/metadata/AnimationResourceMetadata;", cancellable = true)
|
||||||
|
void extractInterpolateAlpha(JsonObject jsonObject, CallbackInfoReturnable<AnimationResourceMetadata> cir) {
|
||||||
|
boolean interpolateAlpha = JsonHelper.getBoolean(jsonObject, "interpolateAlpha", false);
|
||||||
|
AnimationResourceMetadata returnValue = cir.getReturnValue();
|
||||||
|
((AlphaInterpolationHolder)returnValue).colourful_portals$setInterpolateAlpha(interpolateAlpha);
|
||||||
|
cir.setReturnValue(returnValue);
|
||||||
|
cir.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package quimufu.colourful_portals.client.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(MinecraftClient.class)
|
||||||
|
public interface MinecraftClientAccessor {
|
||||||
|
@Accessor
|
||||||
|
void setItemUseCooldown(int itemUseCooldown);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package quimufu.colourful_portals.client.mixin;
|
||||||
|
|
||||||
|
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers;
|
||||||
|
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.FluidRenderer;
|
||||||
|
import me.jellysquid.mods.sodium.client.world.WorldSlice;
|
||||||
|
import net.minecraft.fluid.FluidState;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import quimufu.colourful_portals.client.rendering.fluid.SodiumPortalFluidRenderHandler;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_FLUID;
|
||||||
|
|
||||||
|
@Mixin(FluidRenderer.class)
|
||||||
|
public class SodiumFluidRendererMixin {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private final ThreadLocal<SodiumPortalFluidRenderHandler> sodiumPortalFluidRenderHandler = ThreadLocal.withInitial(SodiumPortalFluidRenderHandler::new);
|
||||||
|
|
||||||
|
@Inject(at = @At("HEAD"), method = "render", cancellable = true, remap = false)
|
||||||
|
private void init(WorldSlice world, FluidState fluidState, BlockPos blockPos, BlockPos offset, ChunkBuildBuffers buffers, CallbackInfo ci) {
|
||||||
|
if (fluidState.isOf(PORTAL_FLUID)) {
|
||||||
|
sodiumPortalFluidRenderHandler.get().render(world, fluidState, blockPos, offset, buffers);
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package quimufu.colourful_portals.client.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.client.resource.metadata.AnimationResourceMetadata;
|
||||||
|
import net.minecraft.client.texture.SpriteContents;
|
||||||
|
import net.minecraft.client.texture.SpriteDimensions;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
|
||||||
|
|
||||||
|
@Mixin(SpriteContents.class)
|
||||||
|
public abstract class SpriteContentsMixin {
|
||||||
|
|
||||||
|
@Unique
|
||||||
|
private final ThreadLocal<Boolean> redirect = ThreadLocal.withInitial(() -> true);
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
@Nullable
|
||||||
|
private SpriteContents.@Nullable Animation animation;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public abstract boolean isPixelTransparent(int frame, int x, int y);
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "createAnimation(Lnet/minecraft/client/texture/SpriteDimensions;IILnet/minecraft/client/resource/metadata/AnimationResourceMetadata;)Lnet/minecraft/client/texture/SpriteContents$Animation;", cancellable = true)
|
||||||
|
void passInterpolateAlpha(SpriteDimensions dimensions, int imageWidth, int imageHeight, AnimationResourceMetadata metadata, CallbackInfoReturnable<AlphaInterpolationHolder> cir) {
|
||||||
|
AlphaInterpolationHolder returnValue = cir.getReturnValue();
|
||||||
|
if (returnValue != null) {
|
||||||
|
returnValue.colourful_portals$setInterpolateAlpha(((AlphaInterpolationHolder) metadata).colourful_portals$isInterpolateAlpha());
|
||||||
|
cir.setReturnValue(returnValue);
|
||||||
|
cir.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "isPixelTransparent(III)Z", cancellable = true)
|
||||||
|
void correctIsPixelTransparent(int frame, int x, int y, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if (redirect.get() && animation != null && ((AlphaInterpolationHolder) animation).colourful_portals$isInterpolateAlpha()) {
|
||||||
|
redirect.set(false);
|
||||||
|
cir.setReturnValue(this.isPixelTransparent(frame, x, y) || this.isPixelTransparent(frame + 1, x, y));
|
||||||
|
redirect.set(true);
|
||||||
|
cir.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package quimufu.colourful_portals.client.particle;
|
||||||
|
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.api.Environment;
|
||||||
|
import net.fabricmc.fabric.api.client.particle.v1.FabricSpriteProvider;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.client.option.ParticlesMode;
|
||||||
|
import net.minecraft.client.particle.ParticleFactory;
|
||||||
|
import net.minecraft.client.particle.ParticleTextureSheet;
|
||||||
|
import net.minecraft.client.particle.SpriteBillboardParticle;
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.particle.ParticleGroup;
|
||||||
|
import net.minecraft.particle.SimpleParticleType;
|
||||||
|
import org.joml.Random;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static net.minecraft.client.option.ParticlesMode.*;
|
||||||
|
|
||||||
|
@Environment(value = EnvType.CLIENT)
|
||||||
|
public class ColourfulAirSparkleParticle extends SpriteBillboardParticle {
|
||||||
|
|
||||||
|
public static ParticleGroup COLOURFUL_AIR_GROUP = new ParticleGroup(0) {
|
||||||
|
private static final Random rnd = new Random(747906141);
|
||||||
|
private static final EnumMap<ParticlesMode, Integer> COUNTS = new EnumMap<>(
|
||||||
|
Map.of(
|
||||||
|
ALL, 1 << 12,
|
||||||
|
DECREASED, 1 << 11,
|
||||||
|
MINIMAL, 1 << 8
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxCount() {
|
||||||
|
Integer count = COUNTS
|
||||||
|
.getOrDefault(MinecraftClient.getInstance().options.getParticles().getValue(), 0);
|
||||||
|
return count / 4 + rnd.nextInt((count * 3) / 4);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final FabricSpriteProvider spriteProvider;
|
||||||
|
private final int variant;
|
||||||
|
|
||||||
|
protected ColourfulAirSparkleParticle(ClientWorld clientWorld,
|
||||||
|
double posX, double posY, double posZ,
|
||||||
|
FabricSpriteProvider spriteProvider
|
||||||
|
|
||||||
|
) {
|
||||||
|
super(clientWorld, posX, posY, posZ);
|
||||||
|
this.spriteProvider = spriteProvider;
|
||||||
|
int variants = spriteProvider.getSprites().size() / 2;
|
||||||
|
variant = random.nextInt(variants);
|
||||||
|
setSprite(spriteProvider.getSprites().get(variant * 2));
|
||||||
|
setMaxAge(20 + random.nextInt(20) + random.nextInt(20));
|
||||||
|
gravityStrength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
if (age == maxAge / 2) {
|
||||||
|
setSprite(spriteProvider.getSprites().get(variant * 2 + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<ParticleGroup> getGroup() {
|
||||||
|
return Optional.ofNullable(COLOURFUL_AIR_GROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParticleTextureSheet getType() {
|
||||||
|
return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParticleFactory<SimpleParticleType> create(FabricSpriteProvider provider) {
|
||||||
|
return (parameters, world, x, y, z, velocityX, velocityY, velocityZ) ->
|
||||||
|
new ColourfulAirSparkleParticle(world, x, y, z, provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
package quimufu.colourful_portals.client.rendering;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import net.minecraft.client.texture.Animator;
|
||||||
|
import net.minecraft.client.texture.NativeImage;
|
||||||
|
import net.minecraft.client.texture.SpriteContents;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This code is mostly copies from minecraft source. It is owned by mojang.
|
||||||
|
*/
|
||||||
|
public class AlphaBlendingAnimator implements Animator {
|
||||||
|
int frame;
|
||||||
|
int currentTime;
|
||||||
|
|
||||||
|
final SpriteContents spriteContents;
|
||||||
|
final SpriteContents.Animation animation;
|
||||||
|
@Nullable
|
||||||
|
private final AlphaBlendingInterpolation interpolation;
|
||||||
|
|
||||||
|
public AlphaBlendingAnimator(SpriteContents spriteContents, SpriteContents.Animation animation) {
|
||||||
|
this.spriteContents = spriteContents;
|
||||||
|
this.animation = animation;
|
||||||
|
this.interpolation = new AlphaBlendingInterpolation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick(int x, int y) {
|
||||||
|
++this.currentTime;
|
||||||
|
SpriteContents.AnimationFrame animationFrame = this.animation.frames.get(this.frame);
|
||||||
|
if (this.currentTime >= animationFrame.time) {
|
||||||
|
int i = animationFrame.index;
|
||||||
|
this.frame = (this.frame + 1) % this.animation.frames.size();
|
||||||
|
this.currentTime = 0;
|
||||||
|
int j = this.animation.frames.get((int)this.frame).index;
|
||||||
|
if (i != j) {
|
||||||
|
this.animation.upload(x, y, j);
|
||||||
|
}
|
||||||
|
} else if (this.interpolation != null) {
|
||||||
|
if (!RenderSystem.isOnRenderThread()) {
|
||||||
|
RenderSystem.recordRenderCall(() -> this.interpolation.apply(x, y, this));
|
||||||
|
} else {
|
||||||
|
this.interpolation.apply(x, y, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (this.interpolation != null) {
|
||||||
|
this.interpolation.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AlphaBlendingInterpolation
|
||||||
|
implements AutoCloseable {
|
||||||
|
private final NativeImage[] images;
|
||||||
|
|
||||||
|
AlphaBlendingInterpolation() {
|
||||||
|
this.images = new NativeImage[spriteContents.mipmapLevelsImages.length];
|
||||||
|
for (int i = 0; i < this.images.length; ++i) {
|
||||||
|
int j = spriteContents.getWidth() >> i;
|
||||||
|
int k = spriteContents.getHeight() >> i;
|
||||||
|
this.images[i] = new NativeImage(j, k, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void apply(int x, int y, AlphaBlendingAnimator animator) {
|
||||||
|
SpriteContents.Animation animation = animator.animation;
|
||||||
|
List<SpriteContents.AnimationFrame> list = animation.frames;
|
||||||
|
SpriteContents.AnimationFrame animationFrame = list.get(animator.frame);
|
||||||
|
double d = 1.0 - (double)animator.currentTime / (double)animationFrame.time;
|
||||||
|
int i = animationFrame.index;
|
||||||
|
int j = list.get((animator.frame + 1) % list.size()).index;
|
||||||
|
if (i != j) {
|
||||||
|
for (int k = 0; k < this.images.length; ++k) {
|
||||||
|
int l = spriteContents.getWidth() >> k;
|
||||||
|
int m = spriteContents.getHeight() >> k;
|
||||||
|
for (int n = 0; n < m; ++n) {
|
||||||
|
for (int o = 0; o < l; ++o) {
|
||||||
|
int p = this.getPixelColor(animation, i, k, o, n);
|
||||||
|
int q = this.getPixelColor(animation, j, k, o, n);
|
||||||
|
int a = this.lerp(d, p >> 24 & 0xFF, q >> 24 & 0xFF);
|
||||||
|
int r = this.lerp(d, p >> 16 & 0xFF, q >> 16 & 0xFF);
|
||||||
|
int s = this.lerp(d, p >> 8 & 0xFF, q >> 8 & 0xFF);
|
||||||
|
int t = this.lerp(d, p & 0xFF, q & 0xFF);
|
||||||
|
this.images[k].setColor(o, n, a << 24 | r << 16 | s << 8 | t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spriteContents.upload(x, y, 0, 0, this.images);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getPixelColor(SpriteContents.Animation animation, int frameIndex, int layer, int x, int y) {
|
||||||
|
return spriteContents.mipmapLevelsImages[layer].getColor(x + (animation.getFrameX(frameIndex) * spriteContents.getWidth() >> layer), y + (animation.getFrameY(frameIndex) * spriteContents.getHeight() >> layer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int lerp(double delta, int to, int from) {
|
||||||
|
return (int)(delta * (double)to + (1.0 - delta) * (double)from);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
for (NativeImage nativeImage : this.images) {
|
||||||
|
nativeImage.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package quimufu.colourful_portals.client.rendering;
|
||||||
|
|
||||||
|
public interface AlphaInterpolationHolder {
|
||||||
|
|
||||||
|
public void colourful_portals$setInterpolateAlpha(boolean interpolateAlpha);
|
||||||
|
public boolean colourful_portals$isInterpolateAlpha();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package quimufu.colourful_portals.client.rendering.fluid;
|
||||||
|
|
||||||
|
import net.minecraft.fluid.FluidState;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public interface CommonPortalFluidRenderer {
|
||||||
|
boolean render(BlockRenderView world,
|
||||||
|
FluidState fluidState,
|
||||||
|
BlockPos pos,
|
||||||
|
Vector3f offset,
|
||||||
|
VertexEater vertexEater);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package quimufu.colourful_portals.client;
|
package quimufu.colourful_portals.client.rendering.fluid;
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
|
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
|
||||||
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
|
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
|
||||||
@@ -8,15 +8,25 @@ import net.minecraft.fluid.FluidState;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
import quimufu.colourful_portals.ColourfulPortalsMod;
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
import quimufu.colourful_portals.portal_fluid.PortalFluid;
|
import quimufu.colourful_portals.portal_fluid.PortalFluid;
|
||||||
|
|
||||||
public class CommonPortalFluidRenderer {
|
public class CommonPortalFluidRendererV1 implements CommonPortalFluidRenderer {
|
||||||
|
|
||||||
|
public static final Quaternionf IDENTITY = new Quaternionf();
|
||||||
private Sprite sprite = null;
|
private Sprite sprite = null;
|
||||||
private final float[] uIu2IvIv2 = new float[4];
|
private final float[] uIu2IvIv2 = new float[4];
|
||||||
|
|
||||||
public boolean render(BlockRenderView world, FluidState fluidState, BlockPos pos, BlockPos offset, VertexEater vertexEater) {
|
@Override
|
||||||
|
public boolean render(BlockRenderView world,
|
||||||
|
FluidState fluidState,
|
||||||
|
BlockPos pos,
|
||||||
|
Vector3f offset,
|
||||||
|
VertexEater vertexEater) {
|
||||||
|
|
||||||
|
offset.sub(0.5f,0.5f,0.5f);
|
||||||
|
|
||||||
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluidState.getFluid());
|
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluidState.getFluid());
|
||||||
sprite = handler.getFluidSprites(null, null, fluidState)[0];
|
sprite = handler.getFluidSprites(null, null, fluidState)[0];
|
||||||
@@ -81,10 +91,10 @@ public class CommonPortalFluidRenderer {
|
|||||||
}
|
}
|
||||||
calcSquishedUVs(axis, 1.f - offsetInSquishedDir, offsetInSquishedDir, direction, (amount & 1) == 1);
|
calcSquishedUVs(axis, 1.f - offsetInSquishedDir, offsetInSquishedDir, direction, (amount & 1) == 1);
|
||||||
} else {
|
} else {
|
||||||
uIu2IvIv2[0] = sprite.getFrameU(0.);
|
uIu2IvIv2[0] = sprite.getFrameU(0.F);
|
||||||
uIu2IvIv2[1] = sprite.getFrameU(16.);
|
uIu2IvIv2[1] = sprite.getFrameU(1.F);
|
||||||
uIu2IvIv2[2] = sprite.getFrameV(0.);
|
uIu2IvIv2[2] = sprite.getFrameV(0.F);
|
||||||
uIu2IvIv2[3] = sprite.getFrameV(16.);
|
uIu2IvIv2[3] = sprite.getFrameV(1.F);
|
||||||
|
|
||||||
}
|
}
|
||||||
drawSide(vertexEater, x, y, z, x2, y2, z2, direction, uIu2IvIv2[0], uIu2IvIv2[2], uIu2IvIv2[1], uIu2IvIv2[3], offset);
|
drawSide(vertexEater, x, y, z, x2, y2, z2, direction, uIu2IvIv2[0], uIu2IvIv2[2], uIu2IvIv2[1], uIu2IvIv2[3], offset);
|
||||||
@@ -98,60 +108,60 @@ public class CommonPortalFluidRenderer {
|
|||||||
switch (axis) {
|
switch (axis) {
|
||||||
case X -> {
|
case X -> {
|
||||||
if (odd) {
|
if (odd) {
|
||||||
uIu2IvIv2[0] = sprite.getFrameU(0.5 + squishedBottom * 16D);
|
uIu2IvIv2[0] = sprite.getFrameU(1.F / 32.F + squishedBottom);
|
||||||
uIu2IvIv2[1] = sprite.getFrameU(0.5 + squishedTop * 16D);
|
uIu2IvIv2[1] = sprite.getFrameU(1.F / 32.F + squishedTop);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
uIu2IvIv2[0] = sprite.getFrameU(0. + squishedBottom * 16D);
|
uIu2IvIv2[0] = sprite.getFrameU(0.F + squishedBottom);
|
||||||
uIu2IvIv2[1] = sprite.getFrameU(squishedTop * 16D);
|
uIu2IvIv2[1] = sprite.getFrameU(squishedTop);
|
||||||
}
|
}
|
||||||
uIu2IvIv2[2] = sprite.getFrameV(0.);
|
uIu2IvIv2[2] = sprite.getFrameV(0.F);
|
||||||
uIu2IvIv2[3] = sprite.getFrameV(16.);
|
uIu2IvIv2[3] = sprite.getFrameV(1.F);
|
||||||
}
|
}
|
||||||
case Z -> {
|
case Z -> {
|
||||||
if (direction.getAxis() == Direction.Axis.Y) {
|
if (direction.getAxis() == Direction.Axis.Y) {
|
||||||
if (odd) {
|
if (odd) {
|
||||||
uIu2IvIv2[2] = sprite.getFrameV(0.5 + squishedBottom * 16D);
|
uIu2IvIv2[2] = sprite.getFrameV(1.F / 32.F + squishedBottom);
|
||||||
uIu2IvIv2[3] = sprite.getFrameV(0.5 + squishedTop * 16D);
|
uIu2IvIv2[3] = sprite.getFrameV(1.F / 32.F + squishedTop);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom * 16D);
|
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom);
|
||||||
uIu2IvIv2[3] = sprite.getFrameV(squishedTop * 16D);
|
uIu2IvIv2[3] = sprite.getFrameV(squishedTop);
|
||||||
}
|
}
|
||||||
uIu2IvIv2[0] = sprite.getFrameU(0.);
|
uIu2IvIv2[0] = sprite.getFrameU(0.F);
|
||||||
uIu2IvIv2[1] = sprite.getFrameU(16.);
|
uIu2IvIv2[1] = sprite.getFrameU(1.F);
|
||||||
} else {
|
} else {
|
||||||
if (odd) {
|
if (odd) {
|
||||||
uIu2IvIv2[0] = sprite.getFrameU(0.5 + squishedBottom * 16D);
|
uIu2IvIv2[0] = sprite.getFrameU(1.F / 32.F + squishedBottom);
|
||||||
uIu2IvIv2[1] = sprite.getFrameU(0.5 + squishedTop * 16D);
|
uIu2IvIv2[1] = sprite.getFrameU(1.F / 32.F + squishedTop);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
uIu2IvIv2[0] = sprite.getFrameU(squishedBottom * 16D);
|
uIu2IvIv2[0] = sprite.getFrameU(squishedBottom);
|
||||||
uIu2IvIv2[1] = sprite.getFrameU(squishedTop * 16D);
|
uIu2IvIv2[1] = sprite.getFrameU(squishedTop);
|
||||||
}
|
}
|
||||||
uIu2IvIv2[2] = sprite.getFrameV(0.);
|
uIu2IvIv2[2] = sprite.getFrameV(0.F);
|
||||||
uIu2IvIv2[3] = sprite.getFrameV(16.);
|
uIu2IvIv2[3] = sprite.getFrameV(1.F);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case Y -> {
|
case Y -> {
|
||||||
if (odd) {
|
if (odd) {
|
||||||
uIu2IvIv2[2] = sprite.getFrameV(0.5 + squishedBottom * 16D);
|
uIu2IvIv2[2] = sprite.getFrameV(1.F / 32.F + squishedBottom);
|
||||||
uIu2IvIv2[3] = sprite.getFrameV(0.5 + squishedTop * 16D);
|
uIu2IvIv2[3] = sprite.getFrameV(1.F / 32.F + squishedTop);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom * 16D);
|
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom);
|
||||||
uIu2IvIv2[3] = sprite.getFrameV(squishedTop * 16D);
|
uIu2IvIv2[3] = sprite.getFrameV(squishedTop);
|
||||||
}
|
}
|
||||||
uIu2IvIv2[0] = sprite.getFrameU(0.);
|
uIu2IvIv2[0] = sprite.getFrameU(0.F);
|
||||||
uIu2IvIv2[1] = sprite.getFrameU(16.);
|
uIu2IvIv2[1] = sprite.getFrameU(1.F);
|
||||||
}
|
}
|
||||||
default -> throw new IllegalStateException("Unexpected value: " + axis);
|
default -> throw new IllegalStateException("Unexpected value: " + axis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void drawSilvers(VertexEater buffers, Direction direction, Direction.Axis axis, float offsetInSquishedDir, float neighbourOffsetInSquishedDir, BlockPos offset, boolean odd) {
|
private void drawSilvers(VertexEater buffers, Direction direction, Direction.Axis axis, float offsetInSquishedDir, float neighbourOffsetInSquishedDir, Vector3f offset, boolean odd) {
|
||||||
|
|
||||||
float x = 0F;
|
float x = 0F;
|
||||||
float x2 = 1F;
|
float x2 = 1F;
|
||||||
@@ -194,7 +204,7 @@ public class CommonPortalFluidRenderer {
|
|||||||
drawSide(buffers, x, y, z, x2, y2, z2, direction, uIu2IvIv2[0], uIu2IvIv2[2], uIu2IvIv2[1], uIu2IvIv2[3], offset);
|
drawSide(buffers, x, y, z, x2, y2, z2, direction, uIu2IvIv2[0], uIu2IvIv2[2], uIu2IvIv2[1], uIu2IvIv2[3], offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawSide(VertexEater vertexConsumer, float x, float y, float z, float x2, float y2, float z2, Direction direction, float frameU, float frameV, float frameU2, float frameV2, BlockPos offset) {
|
private void drawSide(VertexEater vertexConsumer, float x, float y, float z, float x2, float y2, float z2, Direction direction, float frameU, float frameV, float frameU2, float frameV2, Vector3f offset) {
|
||||||
vertexConsumer.setSprite(sprite);
|
vertexConsumer.setSprite(sprite);
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case DOWN -> {
|
case DOWN -> {
|
||||||
@@ -202,50 +212,44 @@ public class CommonPortalFluidRenderer {
|
|||||||
vertexConsumer.eatVertex(x2, y, z, frameU2, frameV2);
|
vertexConsumer.eatVertex(x2, y, z, frameU2, frameV2);
|
||||||
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV);
|
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV);
|
||||||
vertexConsumer.eatVertex(x, y, z2, frameU, frameV);
|
vertexConsumer.eatVertex(x, y, z2, frameU, frameV);
|
||||||
vertexConsumer.drawQuad(offset, direction);
|
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||||
}
|
}
|
||||||
case UP -> {
|
case UP -> {
|
||||||
vertexConsumer.eatVertex(x, y2, z2, frameU, frameV2);
|
vertexConsumer.eatVertex(x, y2, z2, frameU, frameV2);
|
||||||
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV2);
|
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV2);
|
||||||
vertexConsumer.eatVertex(x2, y2, z, frameU2, frameV);
|
vertexConsumer.eatVertex(x2, y2, z, frameU2, frameV);
|
||||||
vertexConsumer.eatVertex(x, y2, z, frameU, frameV);
|
vertexConsumer.eatVertex(x, y2, z, frameU, frameV);
|
||||||
vertexConsumer.drawQuad(offset, direction);
|
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||||
}
|
}
|
||||||
case NORTH -> {
|
case NORTH -> {
|
||||||
vertexConsumer.eatVertex(x, y2, z, frameU, frameV);
|
vertexConsumer.eatVertex(x, y2, z, frameU, frameV);
|
||||||
vertexConsumer.eatVertex(x2, y2, z, frameU2, frameV);
|
vertexConsumer.eatVertex(x2, y2, z, frameU2, frameV);
|
||||||
vertexConsumer.eatVertex(x2, y, z, frameU2, frameV2);
|
vertexConsumer.eatVertex(x2, y, z, frameU2, frameV2);
|
||||||
vertexConsumer.eatVertex(x, y, z, frameU, frameV2);
|
vertexConsumer.eatVertex(x, y, z, frameU, frameV2);
|
||||||
vertexConsumer.drawQuad(offset, direction);
|
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||||
}
|
}
|
||||||
case SOUTH -> {
|
case SOUTH -> {
|
||||||
vertexConsumer.eatVertex(x, y, z2, frameU, frameV);
|
vertexConsumer.eatVertex(x, y, z2, frameU, frameV);
|
||||||
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV);
|
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV);
|
||||||
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV2);
|
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV2);
|
||||||
vertexConsumer.eatVertex(x, y2, z2, frameU, frameV2);
|
vertexConsumer.eatVertex(x, y2, z2, frameU, frameV2);
|
||||||
vertexConsumer.drawQuad(offset, direction);
|
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||||
}
|
}
|
||||||
case WEST -> {
|
case WEST -> {
|
||||||
vertexConsumer.eatVertex(x, y, z, frameU, frameV);
|
vertexConsumer.eatVertex(x, y, z, frameU, frameV);
|
||||||
vertexConsumer.eatVertex(x, y, z2, frameU2, frameV);
|
vertexConsumer.eatVertex(x, y, z2, frameU2, frameV);
|
||||||
vertexConsumer.eatVertex(x, y2, z2, frameU2, frameV2);
|
vertexConsumer.eatVertex(x, y2, z2, frameU2, frameV2);
|
||||||
vertexConsumer.eatVertex(x, y2, z, frameU, frameV2);
|
vertexConsumer.eatVertex(x, y2, z, frameU, frameV2);
|
||||||
vertexConsumer.drawQuad(offset, direction);
|
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||||
}
|
}
|
||||||
case EAST -> {
|
case EAST -> {
|
||||||
vertexConsumer.eatVertex(x2, y2, z, frameU, frameV);
|
vertexConsumer.eatVertex(x2, y2, z, frameU, frameV);
|
||||||
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV);
|
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV);
|
||||||
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV2);
|
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV2);
|
||||||
vertexConsumer.eatVertex(x2, y, z, frameU, frameV2);
|
vertexConsumer.eatVertex(x2, y, z, frameU, frameV2);
|
||||||
vertexConsumer.drawQuad(offset, direction);
|
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface VertexEater {
|
|
||||||
|
|
||||||
void setSprite(Sprite sprite);
|
|
||||||
void eatVertex(float x, float y, float z, float frameU, float frameV);
|
|
||||||
void drawQuad(BlockPos offset, Direction direction);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
package quimufu.colourful_portals.client.rendering.fluid;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
|
||||||
|
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.fluid.FluidState;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
|
import quimufu.colourful_portals.portal_fluid.NullableAxis;
|
||||||
|
import quimufu.colourful_portals.portal_fluid.PortalFluid;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
||||||
|
|
||||||
|
public class CommonPortalFluidRendererV2 implements CommonPortalFluidRenderer {
|
||||||
|
|
||||||
|
private Sprite sprite = null;
|
||||||
|
|
||||||
|
private final float[] heights = new float[9];
|
||||||
|
private final int[] heightsC = new int[9];
|
||||||
|
|
||||||
|
private static final EnumMap<NullableAxis, Quaternionf> quaternions =
|
||||||
|
new EnumMap<>(Map.of(
|
||||||
|
NullableAxis.X, new Quaternionf()
|
||||||
|
.rotationTo(0,1,0,-1,0,0),
|
||||||
|
NullableAxis.Y, new Quaternionf(),//identity
|
||||||
|
NullableAxis.Z, new Quaternionf()
|
||||||
|
.rotationTo(0,1,0,0,0,-1),
|
||||||
|
NullableAxis.NULL, new Quaternionf()//identity
|
||||||
|
));
|
||||||
|
|
||||||
|
private final int[] amounts = new int[9];
|
||||||
|
|
||||||
|
private final float[] quads = new float[]{
|
||||||
|
//Up
|
||||||
|
-0.5f, 0.5f, 0.0f,
|
||||||
|
0.0f, 0.5f, 0.0f,
|
||||||
|
0.0f, 0.5f, -0.5f,
|
||||||
|
-0.5f, 0.5f, -0.5f,
|
||||||
|
|
||||||
|
0.0f, 0.5f, 0.0f,
|
||||||
|
0.5f, 0.5f, 0.0f,
|
||||||
|
0.5f, 0.5f, -0.5f,
|
||||||
|
0.0f, 0.5f, -0.5f,
|
||||||
|
|
||||||
|
-0.5f, 0.5f, 0.5f,
|
||||||
|
0.0f, 0.5f, 0.5f,
|
||||||
|
0.0f, 0.5f, 0.0f,
|
||||||
|
-0.5f, 0.5f, 0.0f,
|
||||||
|
|
||||||
|
0.0f, 0.5f, 0.5f,
|
||||||
|
0.5f, 0.5f, 0.5f,
|
||||||
|
0.5f, 0.5f, 0.0f,
|
||||||
|
0.0f, 0.5f, 0.0f,
|
||||||
|
|
||||||
|
//Down
|
||||||
|
-0.5f, -0.5f, -0.5f,
|
||||||
|
0.0f, -0.5f, -0.5f,
|
||||||
|
0.0f, -0.5f, 0.0f,
|
||||||
|
-0.5f, -0.5f, 0.0f,
|
||||||
|
|
||||||
|
0.0f, -0.5f, -0.5f,
|
||||||
|
0.5f, -0.5f, -0.5f,
|
||||||
|
0.5f, -0.5f, 0.0f,
|
||||||
|
0.0f, -0.5f, 0.0f,
|
||||||
|
|
||||||
|
-0.5f, -0.5f, 0.0f,
|
||||||
|
0.0f, -0.5f, 0.0f,
|
||||||
|
0.0f, -0.5f, 0.5f,
|
||||||
|
-0.5f, -0.5f, 0.5f,
|
||||||
|
|
||||||
|
0.0f, -0.5f, 0.0f,
|
||||||
|
0.5f, -0.5f, 0.0f,
|
||||||
|
0.5f, -0.5f, 0.5f,
|
||||||
|
0.0f, -0.5f, 0.5f,
|
||||||
|
|
||||||
|
//NORTH
|
||||||
|
-0.5f, 0.5f, -0.5f,
|
||||||
|
0.0f, 0.5f, -0.5f,
|
||||||
|
0.0f, -0.5f, -0.5f,
|
||||||
|
-0.5f, -0.5f, -0.5f,
|
||||||
|
|
||||||
|
0.0f, 0.5f, -0.5f,
|
||||||
|
0.5f, 0.5f, -0.5f,
|
||||||
|
0.5f, -0.5f, -0.5f,
|
||||||
|
0.0f, -0.5f, -0.5f,
|
||||||
|
|
||||||
|
//SOUTH
|
||||||
|
-0.5f, -0.5f, 0.5f,
|
||||||
|
0.0f, -0.5f, 0.5f,
|
||||||
|
0.0f, 0.5f, 0.5f,
|
||||||
|
-0.5f, 0.5f, 0.5f,
|
||||||
|
|
||||||
|
0.0f, -0.5f, 0.5f,
|
||||||
|
0.5f, -0.5f, 0.5f,
|
||||||
|
0.5f, 0.5f, 0.5f,
|
||||||
|
0.0f, 0.5f, 0.5f,
|
||||||
|
|
||||||
|
//WEST
|
||||||
|
-0.5f, -0.5f, -0.5f,
|
||||||
|
-0.5f, -0.5f, 0.0f,
|
||||||
|
-0.5f, 0.5f, 0.0f,
|
||||||
|
-0.5f, 0.5f, -0.5f,
|
||||||
|
|
||||||
|
-0.5f, -0.5f, 0.0f,
|
||||||
|
-0.5f, -0.5f, 0.5f,
|
||||||
|
-0.5f, 0.5f, 0.5f,
|
||||||
|
-0.5f, 0.5f, 0.0f,
|
||||||
|
|
||||||
|
//EAST
|
||||||
|
0.5f, 0.5f, -0.5f,
|
||||||
|
0.5f, 0.5f, 0.0f,
|
||||||
|
0.5f, -0.5f, 0.0f,
|
||||||
|
0.5f, -0.5f, -0.5f,
|
||||||
|
|
||||||
|
0.5f, 0.5f, 0.0f,
|
||||||
|
0.5f, 0.5f, 0.5f,
|
||||||
|
0.5f, -0.5f, 0.5f,
|
||||||
|
0.5f, -0.5f, 0.0f,
|
||||||
|
};
|
||||||
|
|
||||||
|
public boolean render(BlockRenderView world, FluidState fluidState, BlockPos pos, Vector3f offset, VertexEater vertexEater) {
|
||||||
|
|
||||||
|
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluidState.getFluid());
|
||||||
|
sprite = handler.getFluidSprites(null, null, fluidState)[0];
|
||||||
|
|
||||||
|
NullableAxis axis = fluidState.get(PortalFluid.AXIS);
|
||||||
|
|
||||||
|
int amount = fluidState.get(PortalFluid.AMOUNT);
|
||||||
|
if (amount == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//get neighbours heights:
|
||||||
|
saveAmounts(world, pos, axis);
|
||||||
|
|
||||||
|
//offsets of corners:
|
||||||
|
Arrays.fill(heights, 0);
|
||||||
|
Arrays.fill(heightsC, 0);
|
||||||
|
for (int a = 0; a < 3; a++) {
|
||||||
|
for (int b = 0; b < 3; b++) {
|
||||||
|
if (amounts[a * 3 + b] <= -1) {
|
||||||
|
if ((a == 1) ^ (b == 1)) {
|
||||||
|
heights[a * 3 + b] += amounts[4] / 32f;
|
||||||
|
heightsC[a * 3 + b]++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (a < 2 && b < 2) {
|
||||||
|
heights[0] += amounts[a * 3 + b] / 32f;
|
||||||
|
heightsC[0]++;
|
||||||
|
}
|
||||||
|
if (a < 2 && b > 0) {
|
||||||
|
heights[2] += amounts[a * 3 + b] / 32f;
|
||||||
|
heightsC[2]++;
|
||||||
|
}
|
||||||
|
if (a > 0 && b < 2) {
|
||||||
|
heights[6] += amounts[a * 3 + b] / 32f;
|
||||||
|
heightsC[6]++;
|
||||||
|
}
|
||||||
|
if (a > 0 && b > 0) {
|
||||||
|
heights[8] += amounts[a * 3 + b] / 32f;
|
||||||
|
heightsC[8]++;
|
||||||
|
}
|
||||||
|
if ((a == 1) ^ (b == 1)) {
|
||||||
|
heights[a * 3 + b] += amounts[a * 3 + b] / 32f;
|
||||||
|
heights[a * 3 + b] += amounts[4] / 32f; //always != -1
|
||||||
|
heightsC[a * 3 + b] += 2;
|
||||||
|
}
|
||||||
|
if ((a == 1) && (b == 1)) {
|
||||||
|
heights[4] = amounts[4] / 32f;//always != -1
|
||||||
|
heightsC[4]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int a = 0; a < 9; a++) {
|
||||||
|
heights[a] = heights[a] / heightsC[a];
|
||||||
|
}
|
||||||
|
for (int i = 0; i < quads.length / 3; i++) {
|
||||||
|
int x = (int) ((quads[i * 3] + 0.5f) * 2);
|
||||||
|
int z = (int) ((quads[i * 3 + 2] + 0.5f) * 2);
|
||||||
|
if (quads[i * 3 + 1] > 0f) {
|
||||||
|
quads[i * 3 + 1] = 0f + heights[x * 3 + z];
|
||||||
|
} else {
|
||||||
|
quads[i * 3 + 1] = 0f - heights[x * 3 + z];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vertexEater.setSprite(sprite);
|
||||||
|
int endUp = 4;
|
||||||
|
int endDown = 8;
|
||||||
|
int endNorth = 10;
|
||||||
|
int endSouth = 12;
|
||||||
|
int endWest = 14;
|
||||||
|
int endEast = 16;
|
||||||
|
for (Direction value : Direction.values()) {
|
||||||
|
switch (value) {
|
||||||
|
case UP -> {
|
||||||
|
for (int i = 0; i < endUp; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
|
||||||
|
vertexEater.eatVertex(
|
||||||
|
quads[i * 12 + j * 3],
|
||||||
|
quads[i * 12 + j * 3 + 1],
|
||||||
|
quads[i * 12 + j * 3 + 2],
|
||||||
|
sprite.getFrameU(quads[i * 12 + j * 3] + 0.5f),
|
||||||
|
sprite.getFrameV(quads[i * 12 + j * 3 + 2] + 0.5f));
|
||||||
|
}
|
||||||
|
vertexEater.drawQuad(offset, quaternions.get(axis), Direction.UP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case DOWN -> {
|
||||||
|
for (int i = endUp; i < endDown; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
vertexEater.eatVertex(
|
||||||
|
quads[i * 12 + j * 3],
|
||||||
|
quads[i * 12 + j * 3 + 1],
|
||||||
|
quads[i * 12 + j * 3 + 2],
|
||||||
|
sprite.getFrameU(quads[i * 12 + j * 3] + 0.5f),
|
||||||
|
sprite.getFrameV(quads[i * 12 + j * 3 + 2] + 0.5f));
|
||||||
|
}
|
||||||
|
vertexEater.drawQuad(offset, quaternions.get(axis), Direction.DOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case NORTH -> {
|
||||||
|
if (amounts[3] >= -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = endDown; i < endNorth; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
vertexEater.eatVertex(
|
||||||
|
quads[i * 12 + j * 3],
|
||||||
|
quads[i * 12 + j * 3 + 1],
|
||||||
|
quads[i * 12 + j * 3 + 2],
|
||||||
|
sprite.getFrameU(quads[i * 12 + j * 3] + 0.5f),
|
||||||
|
sprite.getFrameV(quads[i * 12 + j * 3 + 1] + 0.5f));
|
||||||
|
}
|
||||||
|
vertexEater.drawQuad(offset, quaternions.get(axis), Direction.NORTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case SOUTH -> {
|
||||||
|
if (amounts[5] >= -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = endNorth; i < endSouth; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
vertexEater.eatVertex(
|
||||||
|
quads[i * 12 + j * 3],
|
||||||
|
quads[i * 12 + j * 3 + 1],
|
||||||
|
quads[i * 12 + j * 3 + 2],
|
||||||
|
sprite.getFrameU(quads[i * 12 + j * 3] + 0.5f),
|
||||||
|
sprite.getFrameV(quads[i * 12 + j * 3 + 1] + 0.5f));
|
||||||
|
}
|
||||||
|
vertexEater.drawQuad(offset, quaternions.get(axis), Direction.SOUTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case WEST -> {
|
||||||
|
if (amounts[1] >= -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = endSouth; i < endWest; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
vertexEater.eatVertex(
|
||||||
|
quads[i * 12 + j * 3],
|
||||||
|
quads[i * 12 + j * 3 + 1],
|
||||||
|
quads[i * 12 + j * 3 + 2],
|
||||||
|
sprite.getFrameU(quads[i * 12 + j * 3 + 1] + 0.5f),
|
||||||
|
sprite.getFrameV(quads[i * 12 + j * 3 + 2] + 0.5f));
|
||||||
|
}
|
||||||
|
vertexEater.drawQuad(offset, quaternions.get(axis), Direction.WEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EAST -> {
|
||||||
|
if (amounts[7] >= -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = endWest; i < endEast; i++) {
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
vertexEater.eatVertex(
|
||||||
|
quads[i * 12 + j * 3],
|
||||||
|
quads[i * 12 + j * 3 + 1],
|
||||||
|
quads[i * 12 + j * 3 + 2],
|
||||||
|
sprite.getFrameU(quads[i * 12 + j * 3 + 1] + 0.5f),
|
||||||
|
sprite.getFrameV(quads[i * 12 + j * 3 + 2] + 0.5f));
|
||||||
|
}
|
||||||
|
vertexEater.drawQuad(offset, quaternions.get(axis), Direction.EAST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveAmounts(BlockRenderView world, BlockPos pos, NullableAxis axis) {
|
||||||
|
BlockPos.Mutable curr = new BlockPos.Mutable();
|
||||||
|
switch (axis) {
|
||||||
|
case NULL -> {
|
||||||
|
Arrays.fill(amounts, -2);
|
||||||
|
amounts[4] = 16;
|
||||||
|
}
|
||||||
|
case X -> {
|
||||||
|
for (int y = 0; y < 3; y++) {
|
||||||
|
for (int z = 0; z < 3; z++) {
|
||||||
|
saveAmount(world, pos, curr, 0, y - 1, z - 1, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Y -> {
|
||||||
|
for (int x = 0; x < 3; x++) {
|
||||||
|
for (int z = 0; z < 3; z++) {
|
||||||
|
saveAmount(world, pos, curr, x - 1, 0, z - 1, x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case Z -> {
|
||||||
|
for (int x = 0; x < 3; x++) {
|
||||||
|
for (int y = 0; y < 3; y++) {
|
||||||
|
saveAmount(world, pos, curr, x - 1, y - 1, 0, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveAmount(BlockRenderView world, BlockPos pos, BlockPos.Mutable curr, int dx, int dy, int dz, int major, int minor) {
|
||||||
|
curr.set(pos);
|
||||||
|
curr.move(dx, dy, dz);
|
||||||
|
BlockState blockState = world.getBlockState(curr);
|
||||||
|
FluidState fluidState = blockState
|
||||||
|
.getFluidState();
|
||||||
|
if (fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)) {
|
||||||
|
amounts[major * 3 + minor] = fluidState.get(PortalFluid.AMOUNT);
|
||||||
|
} else {
|
||||||
|
Direction direction = Direction.fromVector(dx, dy, dz);
|
||||||
|
if (direction != null && blockState.isSideSolidFullSquare(world, curr, direction.getOpposite())) {
|
||||||
|
amounts[major * 3 + minor] = -1;
|
||||||
|
} else {
|
||||||
|
amounts[major * 3 + minor] = -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package quimufu.colourful_portals.client;
|
package quimufu.colourful_portals.client.rendering.fluid;
|
||||||
|
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
@@ -13,18 +13,21 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
import quimufu.colourful_portals.ColourfulPortalsMod;
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
|
import quimufu.colourful_portals.client.ColourfulPortalsModClient;
|
||||||
|
|
||||||
@Environment(value = EnvType.CLIENT)
|
@Environment(value = EnvType.CLIENT)
|
||||||
public class PortalFluidRenderHandler implements FluidRenderHandler, CommonPortalFluidRenderer.VertexEater {
|
public class PortalFluidRenderHandler implements FluidRenderHandler, VertexEater {
|
||||||
Identifier PORTAL_FLUID_STILL = new Identifier(ColourfulPortalsMod.MOD_ID, "block/portal_still");
|
Identifier PORTAL_FLUID_STILL = Identifier.of(ColourfulPortalsMod.MOD_ID, "block/portal_still");
|
||||||
|
|
||||||
private final CommonPortalFluidRenderer commonPortalFluidRenderer = new CommonPortalFluidRenderer();
|
|
||||||
private Sprite sprite = null;
|
private Sprite sprite = null;
|
||||||
private final ThreadLocal<VertexConsumer> vertexConsumer = new ThreadLocal<>();
|
private final ThreadLocal<VertexConsumer> vertexConsumer = new ThreadLocal<>();
|
||||||
private final ThreadLocal<Integer> i = new ThreadLocal<>();
|
private final ThreadLocal<Integer> i = new ThreadLocal<>();
|
||||||
|
|
||||||
private final ThreadLocal<float[][]> vertices = new ThreadLocal<>();
|
private final ThreadLocal<float[][]> vertices = new ThreadLocal<>();
|
||||||
|
private final ThreadLocal<Vector3f> tmpVector = ThreadLocal.withInitial(Vector3f::new);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sprite[] getFluidSprites(@Nullable BlockRenderView view, @Nullable BlockPos pos, FluidState state) {
|
public Sprite[] getFluidSprites(@Nullable BlockRenderView view, @Nullable BlockPos pos, FluidState state) {
|
||||||
@@ -39,11 +42,13 @@ public class PortalFluidRenderHandler implements FluidRenderHandler, CommonPorta
|
|||||||
@Override
|
@Override
|
||||||
public void renderFluid(BlockPos pos, BlockRenderView world, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
|
public void renderFluid(BlockPos pos, BlockRenderView world, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
|
||||||
this.vertexConsumer.set(vertexConsumer);
|
this.vertexConsumer.set(vertexConsumer);
|
||||||
BlockPos offset = new BlockPos(pos.getX() & 0xF, pos.getY() & 0xF, pos.getZ() & 0xF);
|
Vector3f offset = new Vector3f(pos.getX() & 0xF, pos.getY() & 0xF, pos.getZ() & 0xF);
|
||||||
|
offset.add(0.5f,0.5f,0.5f);
|
||||||
if (vertices.get() == null) {
|
if (vertices.get() == null) {
|
||||||
vertices.set(new float[4][5]);
|
vertices.set(new float[4][5]);
|
||||||
}
|
}
|
||||||
commonPortalFluidRenderer.render(world, fluidState, pos, offset, this);
|
ColourfulPortalsModClient.FLUID_RENDERER.get()
|
||||||
|
.render(world, fluidState, pos, offset, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -71,19 +76,20 @@ public class PortalFluidRenderHandler implements FluidRenderHandler, CommonPorta
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawQuad(BlockPos offset, Direction direction) {
|
public void drawQuad(Vector3f offset, Quaternionf rotation, Direction direction) {
|
||||||
float offX = offset.getX();
|
float offX = offset.x;
|
||||||
float offY = offset.getY();
|
float offY = offset.y;
|
||||||
float offZ = offset.getZ();
|
float offZ = offset.z;
|
||||||
|
Vector3f f = tmpVector.get();
|
||||||
for (float[] v : vertices.get()) {
|
for (float[] v : vertices.get()) {
|
||||||
|
f.set(v);
|
||||||
|
rotation.transform(f);
|
||||||
vertexConsumer.get()
|
vertexConsumer.get()
|
||||||
.vertex(offX + v[0], offY + v[1], offZ + v[2])
|
.vertex(offX + f.x, offY + f.y, offZ + f.z)
|
||||||
.color(1f, 1f, 1f, 1.0f)
|
.color(1f, 1f, 1f, 1.0f)
|
||||||
.texture(v[3], v[4])
|
.texture(v[3], v[4])
|
||||||
.light(16)
|
.light(16)
|
||||||
.normal(0.0f, 1.0f, 0.0f)
|
.normal(0.0f, 1.0f, 0.0f);
|
||||||
.next();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package quimufu.colourful_portals.client.rendering.fluid;
|
||||||
|
|
||||||
|
import me.jellysquid.mods.sodium.client.model.quad.ModelQuad;
|
||||||
|
import me.jellysquid.mods.sodium.client.model.quad.ModelQuadView;
|
||||||
|
import me.jellysquid.mods.sodium.client.model.quad.ModelQuadViewMutable;
|
||||||
|
import me.jellysquid.mods.sodium.client.model.quad.properties.ModelQuadFacing;
|
||||||
|
import me.jellysquid.mods.sodium.client.render.chunk.compile.ChunkBuildBuffers;
|
||||||
|
import me.jellysquid.mods.sodium.client.render.chunk.compile.buffers.ChunkModelBuilder;
|
||||||
|
import me.jellysquid.mods.sodium.client.render.chunk.terrain.material.DefaultMaterials;
|
||||||
|
import me.jellysquid.mods.sodium.client.render.chunk.terrain.material.Material;
|
||||||
|
import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
|
||||||
|
import net.fabricmc.api.EnvType;
|
||||||
|
import net.fabricmc.api.Environment;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.fluid.FluidState;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import quimufu.colourful_portals.client.ColourfulPortalsModClient;
|
||||||
|
|
||||||
|
@Environment(value = EnvType.CLIENT)
|
||||||
|
public class SodiumPortalFluidRenderHandler implements VertexEater {
|
||||||
|
|
||||||
|
private Sprite sprite = null;
|
||||||
|
private final ModelQuadViewMutable quad = new ModelQuad();
|
||||||
|
|
||||||
|
private final CommonPortalFluidRenderer commonPortalFluidRenderer = ColourfulPortalsModClient.FLUID_RENDERER.get();
|
||||||
|
private final ChunkVertexEncoder.Vertex[] vertices = ChunkVertexEncoder.Vertex.uninitializedQuad();
|
||||||
|
private int i;
|
||||||
|
private ChunkModelBuilder chunkModelBuilder;
|
||||||
|
private Material material;
|
||||||
|
private Vector3f rotBuffer = new Vector3f();
|
||||||
|
|
||||||
|
public SodiumPortalFluidRenderHandler() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean render(BlockRenderView world, FluidState fluidState, BlockPos pos, BlockPos offset, ChunkBuildBuffers buffers) {
|
||||||
|
material = DefaultMaterials.forFluidState(fluidState);
|
||||||
|
this.chunkModelBuilder = buffers.get(material);
|
||||||
|
Vector3f vOffset = new Vector3f(offset.getX(), offset.getY(), offset.getZ());
|
||||||
|
vOffset.add(0.5f, 0.5f, 0.5f);
|
||||||
|
return commonPortalFluidRenderer.render(world, fluidState, pos, vOffset, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSprite(Sprite sprite) {
|
||||||
|
quad.setSprite(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void eatVertex(float x, float y, float z, float u, float v) {
|
||||||
|
quad.setX(i, x);
|
||||||
|
quad.setY(i, y);
|
||||||
|
quad.setZ(i, z);
|
||||||
|
quad.setTexU(i, u);
|
||||||
|
quad.setTexV(i, v);
|
||||||
|
i = (i + 1) % 4;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void drawQuad(Vector3f offset, Quaternionf rot, Direction direction) {
|
||||||
|
writeQuad(chunkModelBuilder, material, offset, quad, rot, ModelQuadFacing.fromDirection(direction), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeQuad(ChunkModelBuilder builder, Material material, Vector3f offset, ModelQuadView quad, Quaternionf rot, ModelQuadFacing facing, boolean flip) {
|
||||||
|
var vertices = this.vertices;
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
var out = vertices[flip ? 3 - i : i];
|
||||||
|
rot.transform(quad.getX(i), quad.getY(i), quad.getZ(i), rotBuffer);
|
||||||
|
out.x = offset.x + rotBuffer.x;
|
||||||
|
out.y = offset.y + rotBuffer.y;
|
||||||
|
out.z = offset.z + rotBuffer.z;
|
||||||
|
out.color = 0xFFFFFFFF;
|
||||||
|
out.u = quad.getTexU(i);
|
||||||
|
out.v = quad.getTexV(i);
|
||||||
|
out.light = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sprite sprite = quad.getSprite();
|
||||||
|
|
||||||
|
if (sprite != null) {
|
||||||
|
builder.addSprite(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
var vertexBuffer = builder.getVertexBuffer(facing);
|
||||||
|
vertexBuffer.push(vertices, material);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package quimufu.colourful_portals.client.rendering.fluid;
|
||||||
|
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import org.joml.Quaternionf;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public interface VertexEater {
|
||||||
|
|
||||||
|
void setSprite(Sprite sprite);
|
||||||
|
|
||||||
|
void eatVertex(float x, float y, float z, float frameU, float frameV);
|
||||||
|
|
||||||
|
void drawQuad(Vector3f offset, Quaternionf quaternionf, Direction direction);
|
||||||
|
}
|
||||||
@@ -1,44 +1,192 @@
|
|||||||
package quimufu.colourful_portals.config;
|
package quimufu.colourful_portals.config;
|
||||||
|
|
||||||
import com.google.common.base.CaseFormat;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import eu.midnightdust.lib.config.MidnightConfig;
|
||||||
import de.siphalor.tweed4.annotated.AConfigEntry;
|
import net.minecraft.registry.RegistryKey;
|
||||||
import de.siphalor.tweed4.annotated.ATweedConfig;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import de.siphalor.tweed4.config.ConfigEnvironment;
|
|
||||||
import de.siphalor.tweed4.config.ConfigScope;
|
|
||||||
//import de.siphalor.tweed4.tailor.cloth.ClothData;
|
|
||||||
import net.minecraft.util.DyeColor;
|
import net.minecraft.util.DyeColor;
|
||||||
//import quimufu.colourful_portals.ColourfulPortalsMod;
|
import net.minecraft.world.World;
|
||||||
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
|
import quimufu.colourful_portals.util.Procedure;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.*;
|
||||||
|
|
||||||
@ATweedConfig(serializer = "tweed4:hjson", scope = ConfigScope.GAME, environment = ConfigEnvironment.SERVER, tailors = {"tweed4:lang_json_descriptions", "tweed4:coat", "tweed4:json_schema"}, casing = CaseFormat.LOWER_HYPHEN)
|
public class ColourfulPortalConfig extends MidnightConfig {
|
||||||
//@ClothData(modid = ColourfulPortalsMod.MOD_ID)
|
|
||||||
public class ColourfulPortalConfig {
|
|
||||||
|
|
||||||
@AConfigEntry(comment =
|
@Entry(category = "integrations", name = "Disable Immersive Portals integration")
|
||||||
"""
|
public static boolean disableImmersivePortals = false;
|
||||||
Allows you to add additional blocks as portalBlocks,
|
|
||||||
assigning them the color of the portal plane.
|
@Comment(category = "portal_colours")
|
||||||
null causes the portals out of that block to have *no* plane.
|
public static Comment explanation1;
|
||||||
Changes in the plane color will only apply to new portals,
|
@Comment(category = "portal_colours")
|
||||||
or on portal update.
|
public static Comment explanation2;
|
||||||
""")
|
|
||||||
public static Map<String, DyeColor> portalBlocks = ImmutableMap.ofEntries(
|
@Entry(category = "portal_colours")
|
||||||
Map.entry("minecraft:white_wool", DyeColor.WHITE),
|
public static List<String> white = Lists.newArrayList("minecraft:white_wool");
|
||||||
Map.entry("minecraft:orange_wool", DyeColor.ORANGE),
|
|
||||||
Map.entry("minecraft:magenta_wool", DyeColor.MAGENTA),
|
@Entry(category = "portal_colours")
|
||||||
Map.entry("minecraft:light_blue_wool", DyeColor.LIGHT_BLUE),
|
public static List<String> orange = Lists.newArrayList("minecraft:orange_wool");
|
||||||
Map.entry("minecraft:yellow_wool", DyeColor.YELLOW),
|
|
||||||
Map.entry("minecraft:lime_wool", DyeColor.LIME),
|
@Entry(category = "portal_colours")
|
||||||
Map.entry("minecraft:pink_wool", DyeColor.PINK),
|
public static List<String> magenta = Lists.newArrayList("minecraft:magenta_wool");
|
||||||
Map.entry("minecraft:gray_wool", DyeColor.GRAY),
|
|
||||||
Map.entry("minecraft:light_gray_wool", DyeColor.LIGHT_GRAY),
|
@Entry(category = "portal_colours")
|
||||||
Map.entry("minecraft:cyan_wool", DyeColor.CYAN),
|
public static List<String> light_blue = Lists.newArrayList("minecraft:light_blue_wool");
|
||||||
Map.entry("minecraft:purple_wool", DyeColor.PURPLE),
|
|
||||||
Map.entry("minecraft:blue_wool", DyeColor.BLUE),
|
@Entry(category = "portal_colours")
|
||||||
Map.entry("minecraft:brown_wool", DyeColor.BROWN),
|
public static List<String> yellow = Lists.newArrayList("minecraft:yellow_wool");
|
||||||
Map.entry("minecraft:green_wool", DyeColor.GREEN),
|
|
||||||
Map.entry("minecraft:red_wool", DyeColor.RED),
|
@Entry(category = "portal_colours")
|
||||||
Map.entry("minecraft:black_wool", DyeColor.BLACK));
|
public static List<String> lime = Lists.newArrayList("minecraft:lime_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> pink = Lists.newArrayList("minecraft:pink_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> gray = Lists.newArrayList("minecraft:gray_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> light_gray = Lists.newArrayList("minecraft:light_gray_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> cyan = Lists.newArrayList("minecraft:cyan_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> purple = Lists.newArrayList("minecraft:purple_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> blue = Lists.newArrayList("minecraft:blue_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> brown = Lists.newArrayList("minecraft:brown_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> green = Lists.newArrayList("minecraft:green_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> red = Lists.newArrayList("minecraft:red_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> black = Lists.newArrayList("minecraft:black_wool");
|
||||||
|
|
||||||
|
@Entry(category = "portal_colours")
|
||||||
|
public static List<String> none = Lists.newArrayList();
|
||||||
|
|
||||||
|
@Entry(category = "visuals")
|
||||||
|
public static boolean blockyPortalFluid = false;
|
||||||
|
|
||||||
|
|
||||||
|
@Entry(category = "colourful_pearl")
|
||||||
|
public static Map<String, Integer> pearlDimensionWeights = new HashMap<>(
|
||||||
|
Map.of("minecraft:overworld", 79,
|
||||||
|
"minecraft:the_nether", 20,
|
||||||
|
"minecraft:the_end", 1)
|
||||||
|
);
|
||||||
|
|
||||||
|
@Entry(category = "colourful_pearl", isSlider = true, min = 0f, max = 1f, precision = 1000)
|
||||||
|
public static double pearlSameDimensionLikelihood = 0.9D;
|
||||||
|
|
||||||
|
@Entry(category = "colourful_pearl", isSlider = true, min = 1 << 4, max = 1 << 16, precision = 10)
|
||||||
|
public static double maxPearlDistance = 1 << 14;
|
||||||
|
|
||||||
|
@Entry(category = "colourful_pearl", isSlider = true, min = 1 << 4, max = 1 << 16, precision = 10)
|
||||||
|
public static double minPearlDistance = 1 << 12;
|
||||||
|
|
||||||
|
private static final ArrayList<Procedure> onWrite = new ArrayList<>(2);
|
||||||
|
|
||||||
|
|
||||||
|
public static Set<String> getAllPortalBlocks() {
|
||||||
|
Set<String> allBlocks = new HashSet<>();
|
||||||
|
allBlocks.addAll(white);
|
||||||
|
allBlocks.addAll(orange);
|
||||||
|
allBlocks.addAll(magenta);
|
||||||
|
allBlocks.addAll(light_blue);
|
||||||
|
allBlocks.addAll(yellow);
|
||||||
|
allBlocks.addAll(lime);
|
||||||
|
allBlocks.addAll(pink);
|
||||||
|
allBlocks.addAll(gray);
|
||||||
|
allBlocks.addAll(light_gray);
|
||||||
|
allBlocks.addAll(cyan);
|
||||||
|
allBlocks.addAll(purple);
|
||||||
|
allBlocks.addAll(blue);
|
||||||
|
allBlocks.addAll(brown);
|
||||||
|
allBlocks.addAll(green);
|
||||||
|
allBlocks.addAll(red);
|
||||||
|
allBlocks.addAll(black);
|
||||||
|
allBlocks.addAll(none);
|
||||||
|
return allBlocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DyeColor colorOf(String block) {
|
||||||
|
if (white.contains(block)) {
|
||||||
|
return DyeColor.WHITE;
|
||||||
|
} else if (orange.contains(block)) {
|
||||||
|
return DyeColor.ORANGE;
|
||||||
|
} else if (magenta.contains(block)) {
|
||||||
|
return DyeColor.MAGENTA;
|
||||||
|
} else if (light_blue.contains(block)) {
|
||||||
|
return DyeColor.LIGHT_BLUE;
|
||||||
|
} else if (yellow.contains(block)) {
|
||||||
|
return DyeColor.YELLOW;
|
||||||
|
} else if (lime.contains(block)) {
|
||||||
|
return DyeColor.LIME;
|
||||||
|
} else if (pink.contains(block)) {
|
||||||
|
return DyeColor.PINK;
|
||||||
|
} else if (gray.contains(block)) {
|
||||||
|
return DyeColor.GRAY;
|
||||||
|
} else if (light_gray.contains(block)) {
|
||||||
|
return DyeColor.LIGHT_GRAY;
|
||||||
|
} else if (cyan.contains(block)) {
|
||||||
|
return DyeColor.CYAN;
|
||||||
|
} else if (purple.contains(block)) {
|
||||||
|
return DyeColor.PURPLE;
|
||||||
|
} else if (blue.contains(block)) {
|
||||||
|
return DyeColor.BLUE;
|
||||||
|
} else if (brown.contains(block)) {
|
||||||
|
return DyeColor.BROWN;
|
||||||
|
} else if (green.contains(block)) {
|
||||||
|
return DyeColor.GREEN;
|
||||||
|
} else if (red.contains(block)) {
|
||||||
|
return DyeColor.RED;
|
||||||
|
} else if (black.contains(block)) {
|
||||||
|
return DyeColor.BLACK;
|
||||||
|
} else if (none.contains(block)) {
|
||||||
|
return null; // Fully transparent portal
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Invalid portal block: " + block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeChanges(String modid) {
|
||||||
|
super.writeChanges(modid);
|
||||||
|
for (Procedure procedure : onWrite) {
|
||||||
|
procedure.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerListener(Procedure p) {
|
||||||
|
onWrite.add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addMissingDimensionsToConfig(MinecraftServer server) {
|
||||||
|
Set<RegistryKey<World>> worlds = server.getWorldRegistryKeys();
|
||||||
|
boolean changed = false;
|
||||||
|
for (RegistryKey<World> world : worlds) {
|
||||||
|
if (!pearlDimensionWeights.containsKey(world.getValue().toString())) {
|
||||||
|
pearlDimensionWeights.put(world.getValue().toString(), 1);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxPearlDistance < minPearlDistance) {
|
||||||
|
double min = maxPearlDistance;
|
||||||
|
maxPearlDistance = minPearlDistance;
|
||||||
|
minPearlDistance = min;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
write(ColourfulPortalsMod.MOD_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package quimufu.colourful_portals.entity;
|
||||||
|
|
||||||
|
import net.minecraft.client.world.ClientWorld;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.EntityType;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.projectile.thrown.ThrownItemEntity;
|
||||||
|
import net.minecraft.item.Item;
|
||||||
|
import net.minecraft.predicate.entity.EntityPredicates;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.util.hit.HitResult;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
|
import quimufu.colourful_portals.util.TeleportParticleHelper;
|
||||||
|
import quimufu.colourful_portals.util.TeleportHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ColourfulPearlEntity
|
||||||
|
extends ThrownItemEntity {
|
||||||
|
|
||||||
|
public ColourfulPearlEntity(EntityType<? extends ColourfulPearlEntity> entityType, World world) {
|
||||||
|
super(entityType, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColourfulPearlEntity(World world, LivingEntity owner) {
|
||||||
|
super(ColourfulPortalsMod.COLOURFUL_PEARL_ENTITY_TYPE, owner, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Item getDefaultItem() {
|
||||||
|
return ColourfulPortalsMod.COLOURFUL_PEARL_ITEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRemoved() {
|
||||||
|
if (this.getRemovalReason() == RemovalReason.DISCARDED) {
|
||||||
|
if (getWorld() instanceof ClientWorld clientWorld) {
|
||||||
|
clientWorld.playSound(
|
||||||
|
this.getX(), this.getY(), this.getZ(),
|
||||||
|
ColourfulPortalsMod.TELEPORT_AWAY_SOUND,
|
||||||
|
SoundCategory.BLOCKS,
|
||||||
|
100F, 1.25F,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Entity> entities = getWorld()
|
||||||
|
.getOtherEntities(this, this.getBoundingBox().expand(5));
|
||||||
|
|
||||||
|
entities.stream()
|
||||||
|
.filter(e -> e.squaredDistanceTo(this) < 25)
|
||||||
|
.forEach(entity -> TeleportParticleHelper.spawnTeleportationParticles(entity, getWorld()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCollision(HitResult hitResult) {
|
||||||
|
if (!(getWorld() instanceof ServerWorld serverWorld)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isRemoved()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Entity> entities = getWorld()
|
||||||
|
.getOtherEntities(this, this.getBoundingBox().expand(5), EntityPredicates.EXCEPT_SPECTATOR
|
||||||
|
.and(entity -> entity instanceof LivingEntity)
|
||||||
|
.and(entity -> !entity.getType().isIn(ColourfulPortalsMod.COLOURFUL_PEARL_NOT_TELEPORTABLE)));
|
||||||
|
|
||||||
|
entities.stream()
|
||||||
|
.filter(e -> e.squaredDistanceTo(this) < 25)
|
||||||
|
.forEach(entity -> {
|
||||||
|
Vec3d pos = this.getPos();
|
||||||
|
TeleportHelper.markForTeleport(entity,pos.toVector3f(), serverWorld);
|
||||||
|
});
|
||||||
|
this.discard();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,457 @@
|
|||||||
|
package quimufu.colourful_portals.general_util;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
||||||
|
|
||||||
|
public class LinkedList<E> implements List<E> {
|
||||||
|
|
||||||
|
Node<E> headNode = null;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return indexOf(o) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
Node<E> node = headNode;
|
||||||
|
int size = 0;
|
||||||
|
while (node != null) {
|
||||||
|
size++;
|
||||||
|
node = node.getNext();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return headNode == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return new LLIterator<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
Object[] objects = new Object[size()];
|
||||||
|
Node<E> node = headNode;
|
||||||
|
int index = 0;
|
||||||
|
while (node != null) {
|
||||||
|
objects[index] = node.getValue();
|
||||||
|
node = node.getNext();
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> T @NotNull [] toArray(T[] a) {
|
||||||
|
int size = this.size();
|
||||||
|
if (size > a.length) {
|
||||||
|
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
|
||||||
|
}
|
||||||
|
Object[] result = a;
|
||||||
|
Node<E> node = headNode;
|
||||||
|
int index = 0;
|
||||||
|
while (node != null) {
|
||||||
|
result[index] = node.getValue();
|
||||||
|
node = node.getNext();
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
if (size != a.length) {
|
||||||
|
a[index] = null;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E e) {
|
||||||
|
if (headNode == null) {
|
||||||
|
headNode = new Node<>(e, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Node<E> node = getLastNode(headNode);
|
||||||
|
node.insertAfter(e);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull Node<E> getLastNode(@NotNull Node<E> start) {
|
||||||
|
Node<E> node = start;
|
||||||
|
while (node.getNext() != null) {
|
||||||
|
node = node.getNext();
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(@NotNull Collection<?> c) {
|
||||||
|
for (Object o : c) {
|
||||||
|
if (!contains(o)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(@NotNull Collection<? extends E> c) {
|
||||||
|
if (headNode == null) {
|
||||||
|
headNode = new Node<>(null, this);
|
||||||
|
Node<E> startNode = new Node<>(null, this);
|
||||||
|
Node<E> currNode = startNode;
|
||||||
|
for (E o : c) {
|
||||||
|
currNode.insertAfter(o);
|
||||||
|
currNode = currNode.getNext();
|
||||||
|
}
|
||||||
|
startNode.remove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Node<E> currNode = getLastNode(headNode);
|
||||||
|
for (E o : c) {
|
||||||
|
currNode.insertAfter(o);
|
||||||
|
currNode = currNode.getNext();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int index, @NotNull Collection<? extends E> c) {
|
||||||
|
if (c.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Node<E> node = getNode(index);
|
||||||
|
for (E e : c) {
|
||||||
|
node.insertBefore(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(@NotNull Collection<?> c) {
|
||||||
|
if (headNode == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean ret = false;
|
||||||
|
Node<E> currNode = headNode;
|
||||||
|
while (currNode != null) {
|
||||||
|
if (c.contains(currNode.getValue())) {
|
||||||
|
currNode.remove();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
currNode = currNode.getNext();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(@NotNull Collection<?> c) {
|
||||||
|
if (headNode == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean ret = false;
|
||||||
|
Node<E> currNode = headNode;
|
||||||
|
while (currNode != null) {
|
||||||
|
if (!c.contains(currNode.getValue())) {
|
||||||
|
currNode.remove();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
currNode = currNode.getNext();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
Node<E> node = headNode;
|
||||||
|
while (node != null) {
|
||||||
|
if (Objects.equals(node.getValue(), o)) {
|
||||||
|
node.remove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
node = node.getNext();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public E get(int index) {
|
||||||
|
return getNode(index).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node<E> getNode(int index) {
|
||||||
|
Node<E> node = headNode;
|
||||||
|
for (int i = 0; i < index; i++) {
|
||||||
|
if (node == null)
|
||||||
|
throw new IndexOutOfBoundsException(index);
|
||||||
|
node = node.getNext();
|
||||||
|
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E set(int index, E element) {
|
||||||
|
getNode(index).setValue(element);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int index, E element) {
|
||||||
|
getNode(index).insertBefore(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public E remove(int index) {
|
||||||
|
Node<E> node = getNode(index);
|
||||||
|
node.remove();
|
||||||
|
return node.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
int index = 0;
|
||||||
|
Node<E> node = headNode;
|
||||||
|
while (node != null) {
|
||||||
|
if (Objects.equals(node.getValue(), o)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
node = node.getNext();
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
int ret = -1;
|
||||||
|
int index = 0;
|
||||||
|
Node<E> node = headNode;
|
||||||
|
while (node != null) {
|
||||||
|
if (Objects.equals(node.getValue(), o)) {
|
||||||
|
ret = index;
|
||||||
|
}
|
||||||
|
node = node.getNext();
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ListIterator<E> listIterator() {
|
||||||
|
|
||||||
|
return new LLLIterator<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public ListIterator<E> listIterator(int index) {
|
||||||
|
LLLIterator<E> iterator = new LLLIterator<>(this);
|
||||||
|
iterator.nextNode = getNode(index);
|
||||||
|
iterator.previousNode = iterator.nextNode.getPrevious();
|
||||||
|
return iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public List<E> subList(int fromIndex, int toIndex) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Node<E> getNodeOf(E content) {
|
||||||
|
Node<E> node = headNode;
|
||||||
|
while (node != null) {
|
||||||
|
if (Objects.equals(node.getValue(), content)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node = node.getNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LLIterator<E> implements Iterator<E> {
|
||||||
|
|
||||||
|
private Node<E> nextNode;
|
||||||
|
|
||||||
|
public LLIterator(LinkedList<E> linkedList) {
|
||||||
|
nextNode = linkedList.headNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return nextNode != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E next() {
|
||||||
|
if (nextNode == null)
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
|
||||||
|
E value = nextNode.getValue();
|
||||||
|
nextNode = nextNode.getNext();
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LLLIterator<E> implements ListIterator<E> {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Node<E> nextNode;
|
||||||
|
@Nullable
|
||||||
|
private Node<E> previousNode;
|
||||||
|
private boolean lastNext = true;
|
||||||
|
|
||||||
|
private final LinkedList<E> linkedList;
|
||||||
|
|
||||||
|
public LLLIterator(LinkedList<E> linkedList) {
|
||||||
|
nextNode = linkedList.headNode;
|
||||||
|
previousNode = null;
|
||||||
|
this.linkedList = linkedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return nextNode != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E next() {
|
||||||
|
tryFixOrphans();
|
||||||
|
if (nextNode == null)
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
|
||||||
|
E value = nextNode.getValue();
|
||||||
|
previousNode = nextNode;
|
||||||
|
nextNode = nextNode.getNext();
|
||||||
|
lastNext = true;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasPrevious() {
|
||||||
|
return previousNode != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E previous() {
|
||||||
|
tryFixOrphans();
|
||||||
|
if (previousNode == null)
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
E value = previousNode.getValue();
|
||||||
|
nextNode = previousNode;
|
||||||
|
previousNode = previousNode.getPrevious();
|
||||||
|
lastNext = false;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int nextIndex() {
|
||||||
|
tryFixOrphans();
|
||||||
|
if(nextNode == null)
|
||||||
|
return linkedList.size();
|
||||||
|
return linkedList.indexOfNode(nextNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int previousIndex() {
|
||||||
|
tryFixOrphans();
|
||||||
|
if(previousNode == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return linkedList.indexOfNode(previousNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
tryFixOrphans();
|
||||||
|
if(lastNext) {
|
||||||
|
if(previousNode == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
previousNode.remove();
|
||||||
|
previousNode = previousNode.getPrevious();
|
||||||
|
} else {
|
||||||
|
if(nextNode == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
nextNode.remove();
|
||||||
|
nextNode = nextNode.getNext();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(E e) {
|
||||||
|
tryFixOrphans();
|
||||||
|
if(lastNext) {
|
||||||
|
if(previousNode == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
previousNode.setValue(e);
|
||||||
|
} else {
|
||||||
|
if(nextNode == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
nextNode.setValue(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(E e) {
|
||||||
|
tryFixOrphans();
|
||||||
|
if(nextNode != null) {
|
||||||
|
nextNode.insertBefore(e);
|
||||||
|
} else if(previousNode != null) {
|
||||||
|
previousNode.insertAfter(e);
|
||||||
|
} else {
|
||||||
|
// empty!
|
||||||
|
linkedList.add(e);
|
||||||
|
nextNode = linkedList.headNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryFixOrphans() {
|
||||||
|
if (previousNode != null && previousNode.orphaned()){
|
||||||
|
if(nextNode != null && !nextNode.orphaned()){
|
||||||
|
previousNode = nextNode.getPrevious();
|
||||||
|
} else {
|
||||||
|
throw new ConcurrentModificationException("Node was concurrently deleted!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (nextNode != null && nextNode.orphaned()){
|
||||||
|
if(previousNode != null && !previousNode.orphaned()){
|
||||||
|
nextNode = previousNode.getNext();
|
||||||
|
} else {
|
||||||
|
throw new ConcurrentModificationException("Node was concurrently deleted!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int indexOfNode(Node<E> needle) {
|
||||||
|
int index = 0;
|
||||||
|
Node<E> node = headNode;
|
||||||
|
while (node != null) {
|
||||||
|
if (node == needle) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
node = node.getNext();
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package quimufu.colourful_portals.general_util;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public class Node<E> {
|
||||||
|
|
||||||
|
private Node<E> next = null;
|
||||||
|
private Node<E> previous = null;
|
||||||
|
private E value;
|
||||||
|
|
||||||
|
public LinkedList<E> getPartOf() {
|
||||||
|
return partOf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final LinkedList<E> partOf;
|
||||||
|
private boolean orphaned = false;
|
||||||
|
|
||||||
|
protected Node(E value, LinkedList<E> partOf) {
|
||||||
|
this.value = value;
|
||||||
|
this.partOf = partOf;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node<E> getNext() {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNext(@Nullable Node<E> next) {
|
||||||
|
if (next != null) {
|
||||||
|
next.previous = this;
|
||||||
|
}
|
||||||
|
if(next == this){
|
||||||
|
throw new IllegalArgumentException("cycles are not allowed");
|
||||||
|
}
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public E getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(E value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node<E> getPrevious() {
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrevious(@Nullable Node<E> previous) {
|
||||||
|
if (previous != null) {
|
||||||
|
previous.next = this;
|
||||||
|
}
|
||||||
|
this.previous = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove() {
|
||||||
|
if (partOf.headNode == this) {
|
||||||
|
if (next != null) {
|
||||||
|
partOf.headNode = next;
|
||||||
|
} else {
|
||||||
|
partOf.headNode = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (previous != null) {
|
||||||
|
previous.setNext(next);
|
||||||
|
}
|
||||||
|
if (next != null) {
|
||||||
|
next.setPrevious(previous);
|
||||||
|
}
|
||||||
|
orphaned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertBefore(E element) {
|
||||||
|
Node<E> newNode = new Node<>(element, partOf);
|
||||||
|
newNode.setPrevious(this.previous);
|
||||||
|
newNode.setNext(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertAfter(E element) {
|
||||||
|
Node<E> newNode = new Node<>(element, partOf);
|
||||||
|
newNode.setNext(this.next);
|
||||||
|
newNode.setPrevious(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean orphaned() {
|
||||||
|
return orphaned;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package quimufu.colourful_portals.general_util;
|
||||||
|
|
||||||
|
|
||||||
|
import org.joml.Random;
|
||||||
|
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
public class WeightedSelector <E> {
|
||||||
|
|
||||||
|
private final TreeMap<Integer, E> backingMap = new TreeMap<>();
|
||||||
|
|
||||||
|
private int total = 0;
|
||||||
|
|
||||||
|
public void add(E object, int weight){
|
||||||
|
if(weight <= 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
backingMap.put(total, object);
|
||||||
|
total += weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
total = 0;
|
||||||
|
backingMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public E getWeighted(Random random) {
|
||||||
|
if(total == 0){
|
||||||
|
throw new IllegalStateException("tried to receive Weighted Result from empty Selector");
|
||||||
|
}
|
||||||
|
return backingMap.floorEntry(random.nextInt(total)).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package quimufu.colourful_portals.item;
|
||||||
|
|
||||||
|
import net.minecraft.advancement.criterion.Criteria;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.component.DataComponentTypes;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.item.*;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.sound.SoundEvent;
|
||||||
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
import net.minecraft.stat.Stats;
|
||||||
|
import net.minecraft.util.*;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.event.GameEvent;
|
||||||
|
import quimufu.colourful_portals.ColourfulAirBlock;
|
||||||
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
|
import quimufu.colourful_portals.client.mixin.MinecraftClientAccessor;
|
||||||
|
|
||||||
|
public class ColourfulAirBottleItem
|
||||||
|
extends AliasedBlockItem {
|
||||||
|
private static final int MAX_USE_TIME = 40;
|
||||||
|
|
||||||
|
public ColourfulAirBottleItem(Block block, Settings settings) {
|
||||||
|
super(block, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) {
|
||||||
|
if (!tryPlaceAtHead(stack, world, user)) {
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
user.emitGameEvent(GameEvent.DRINK);
|
||||||
|
|
||||||
|
if (user instanceof PlayerEntity playerEntity) {
|
||||||
|
stack = ItemUsage.exchangeStack(stack, playerEntity, new ItemStack(Items.GLASS_BOTTLE));
|
||||||
|
if(world.isClient){
|
||||||
|
((MinecraftClientAccessor)MinecraftClient.getInstance())
|
||||||
|
.setItemUseCooldown(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user instanceof ServerPlayerEntity serverPlayerEntity) {
|
||||||
|
Criteria.CONSUME_ITEM.trigger(serverPlayerEntity, stack);
|
||||||
|
serverPlayerEntity.incrementStat(Stats.USED.getOrCreateStat(this));
|
||||||
|
}
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryPlaceAtHead(ItemStack stack, World world, LivingEntity user) {
|
||||||
|
BlockPos headPos = BlockPos.ofFloored(user.getEyePos());
|
||||||
|
if (world.getBlockState(headPos).isIn(ColourfulPortalsMod.COLOURFUL_PEARL_REPLACEABLE_BLOCK_TAG)) {
|
||||||
|
DyeColor color = stack.getOrDefault(DataComponentTypes.BASE_COLOR, DyeColor.BLACK);
|
||||||
|
return world.setBlockState(headPos, getBlock().getDefaultState()
|
||||||
|
.withIfExists(ColourfulAirBlock.DYE_COLOR, color),
|
||||||
|
Block.NOTIFY_ALL_AND_REDRAW);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ActionResult useOnBlock(ItemUsageContext context) {
|
||||||
|
if (context.getPlayer() != null && context.getPlayer().isSneaking()) {
|
||||||
|
return ActionResult.PASS;
|
||||||
|
}
|
||||||
|
return super.useOnBlock(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxUseTime(ItemStack stack, LivingEntity user) {
|
||||||
|
return MAX_USE_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UseAction getUseAction(ItemStack stack) {
|
||||||
|
return UseAction.DRINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoundEvent getDrinkSound() {
|
||||||
|
//todo
|
||||||
|
return SoundEvents.ITEM_HONEY_BOTTLE_DRINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoundEvent getEatSound() {
|
||||||
|
//todo
|
||||||
|
return SoundEvents.ITEM_HONEY_BOTTLE_DRINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
|
||||||
|
return ItemUsage.consumeHeldItem(world, user, hand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package quimufu.colourful_portals.item;
|
||||||
|
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.item.Item;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.item.ItemUsage;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.sound.SoundEvent;
|
||||||
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
import net.minecraft.stat.Stats;
|
||||||
|
import net.minecraft.util.Hand;
|
||||||
|
import net.minecraft.util.TypedActionResult;
|
||||||
|
import net.minecraft.util.UseAction;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.event.GameEvent;
|
||||||
|
import quimufu.colourful_portals.entity.ColourfulPearlEntity;
|
||||||
|
import quimufu.colourful_portals.util.TeleportParticleHelper;
|
||||||
|
import quimufu.colourful_portals.util.TeleportHelper;
|
||||||
|
|
||||||
|
public class ColourfulPearlItem
|
||||||
|
extends Item {
|
||||||
|
private static final int MAX_USE_TIME = 5;
|
||||||
|
|
||||||
|
public ColourfulPearlItem(Item.Settings settings) {
|
||||||
|
super(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack finishUsing(ItemStack stack, World world, LivingEntity user) {
|
||||||
|
if(user instanceof PlayerEntity player){
|
||||||
|
player.getItemCooldownManager().set(this, 50);
|
||||||
|
}
|
||||||
|
if (world instanceof ServerWorld serverWorld) {
|
||||||
|
TeleportHelper.markForTeleport(user, user.getPos().toVector3f(), serverWorld);
|
||||||
|
}
|
||||||
|
stack.decrementUnlessCreative(1, user);
|
||||||
|
user.emitGameEvent(GameEvent.EAT);
|
||||||
|
return stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UseAction getUseAction(ItemStack stack) {
|
||||||
|
return UseAction.DRINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoundEvent getDrinkSound() {
|
||||||
|
//todo
|
||||||
|
return SoundEvents.ITEM_HONEY_BOTTLE_DRINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SoundEvent getEatSound() {
|
||||||
|
//todo
|
||||||
|
return SoundEvents.ITEM_HONEY_BOTTLE_DRINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxUseTime(ItemStack stack, LivingEntity user) {
|
||||||
|
return MAX_USE_TIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) {
|
||||||
|
if (user.isSneaking()) {
|
||||||
|
TeleportParticleHelper.spawnTeleportationParticles(user, world);
|
||||||
|
return ItemUsage.consumeHeldItem(world, user, hand);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStack stackInHand = user.getStackInHand(hand);
|
||||||
|
world.playSound(null, user.getX(), user.getY(), user.getZ(), SoundEvents.ENTITY_ENDER_PEARL_THROW, SoundCategory.NEUTRAL, 0.5f, 0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f));
|
||||||
|
user.getItemCooldownManager().set(this, 50);
|
||||||
|
if (!world.isClient) {
|
||||||
|
ColourfulPearlEntity colourfulPearlEntity = new ColourfulPearlEntity(world, user);
|
||||||
|
colourfulPearlEntity.setItem(stackInHand);
|
||||||
|
colourfulPearlEntity.setVelocity(user, user.getPitch(), user.getYaw(), 0.0f, 1.5f, 1.0f);
|
||||||
|
world.spawnEntity(colourfulPearlEntity);
|
||||||
|
}
|
||||||
|
user.incrementStat(Stats.USED.getOrCreateStat(this));
|
||||||
|
stackInHand.decrementUnlessCreative(1, user);
|
||||||
|
return TypedActionResult.success(stackInHand, world.isClient());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package quimufu.colourful_portals.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
import quimufu.colourful_portals.util.RaycastHelper;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
|
||||||
|
|
||||||
|
@Mixin(ServerWorld.class)
|
||||||
|
public abstract class BlockChangeAndEntityMovementMixin {
|
||||||
|
@Unique
|
||||||
|
private static final List<Direction> HORIZONTAL = Direction.stream().filter(d -> d.getAxis().isHorizontal()).toList();
|
||||||
|
|
||||||
|
// @Unique
|
||||||
|
// private final ThreadLocal<Map<EntityType<?>, Entity>> entities =
|
||||||
|
// ThreadLocal.withInitial(() -> HashMap.newHashMap(Registries.ENTITY_TYPE.size()));
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public abstract String toString();
|
||||||
|
|
||||||
|
// @Inject(at = @At(value = "HEAD"), method = "tickEntity")
|
||||||
|
// private void cpm_beforeEntityTick(Entity entity, CallbackInfo ci) {
|
||||||
|
// if(PORTAL_MANAGER.getLinkingSystem().needsMovementCallback()){
|
||||||
|
// Entity copy = entities.get().computeIfAbsent(entity.getType(), t -> t.create(null));
|
||||||
|
// copy.copyFrom(entity);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public abstract boolean isChunkLoaded(long chunkPos);
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "tickEntity")
|
||||||
|
private void cpm_afterEntityTick(Entity entity, CallbackInfo ci) {
|
||||||
|
if (PORTAL_MANAGER.getLinkingSystem().needsMovementCallback()) {
|
||||||
|
Vec3d prevEntityPos;
|
||||||
|
if (entity instanceof ServerPlayerEntity serverPlayerEntity) {
|
||||||
|
ServerPlayNetworkHandlerAccessor spnha = (ServerPlayNetworkHandlerAccessor) serverPlayerEntity.networkHandler;
|
||||||
|
prevEntityPos = new Vec3d(spnha.getLastTickX(), spnha.getLastTickY(), spnha.getLastTickZ());
|
||||||
|
} else {
|
||||||
|
prevEntityPos = new Vec3d(entity.prevX, entity.prevY, entity.prevZ);
|
||||||
|
}
|
||||||
|
Vec3d entityPos = entity.getPos();
|
||||||
|
ServerWorld world = (ServerWorld) (Object) this;
|
||||||
|
List<BlockPos> passedBlocks = RaycastHelper.passedBlocks(prevEntityPos, entityPos);
|
||||||
|
for (BlockPos passedBlock : passedBlocks) {
|
||||||
|
if (PORTAL_MANAGER.onMovementThroughBlock(entity, passedBlock, world)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(at = @At("RETURN"), method = "onBlockChanged")
|
||||||
|
private void cpm_afterOnBlockChanged(BlockPos pos, BlockState oldBlock, BlockState newBlock, CallbackInfo info) {
|
||||||
|
ServerWorld world = (ServerWorld) (Object) this;
|
||||||
|
if (world.isDebugWorld())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// This code is injected into the end of ServerWorld.onBlockChanged()V
|
||||||
|
if (oldBlock.getBlock() != newBlock.getBlock()) {
|
||||||
|
if (PORTAL_BLOCKS.contains(Registries.BLOCK.getId(newBlock.getBlock()))) {
|
||||||
|
LOGGER.debug("onBlockNew {} -> {}", oldBlock, newBlock);
|
||||||
|
Identifier blockId = Registries.BLOCK.getId(newBlock.getBlock());
|
||||||
|
synchronized (PORTAL_MANAGER.pendingUpdates){
|
||||||
|
PORTAL_MANAGER.pendingUpdates.add(() ->
|
||||||
|
PORTAL_MANAGER.onPortalBlockPlaced(world, pos, blockId));
|
||||||
|
}
|
||||||
|
if(!PORTAL_MANAGER.blockUpdatesPending.getAndSet(true)){
|
||||||
|
world.getServer().execute(PORTAL_MANAGER::runPendingUpdates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PORTAL_BLOCKS.contains(Registries.BLOCK.getId(oldBlock.getBlock()))) {
|
||||||
|
LOGGER.debug("onBlockOld {} -> {}", oldBlock, newBlock);
|
||||||
|
Identifier blockId = Registries.BLOCK.getId(oldBlock.getBlock());
|
||||||
|
synchronized (PORTAL_MANAGER.pendingUpdates){
|
||||||
|
PORTAL_MANAGER.pendingUpdates.add(() ->
|
||||||
|
PORTAL_MANAGER.onPortalBlockBroken(world, pos, blockId));
|
||||||
|
}
|
||||||
|
if(!PORTAL_MANAGER.blockUpdatesPending.getAndSet(true)){
|
||||||
|
world.getServer().execute(PORTAL_MANAGER::runPendingUpdates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package quimufu.colourful_portals.mixin;
|
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.registry.Registries;
|
|
||||||
import net.minecraft.server.world.ServerWorld;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
import quimufu.colourful_portals.portal.PortalManager;
|
|
||||||
|
|
||||||
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
|
||||||
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_BLOCKS;
|
|
||||||
|
|
||||||
@Mixin(ServerWorld.class)
|
|
||||||
public class BlockChangeMixin {
|
|
||||||
|
|
||||||
@Inject(at = @At("RETURN"), method = "onBlockChanged")
|
|
||||||
private void init(BlockPos pos, BlockState oldBlock, BlockState newBlock, CallbackInfo info) {
|
|
||||||
ServerWorld world = (ServerWorld) (Object) this;
|
|
||||||
if(world.isDebugWorld()|| !world.isChunkLoaded(pos))
|
|
||||||
return;
|
|
||||||
// This code is injected into the end of ServerWorld.onBlockChanged()V
|
|
||||||
if (oldBlock.getBlock() != newBlock.getBlock()) {
|
|
||||||
if (PORTAL_BLOCKS.contains(Registries.BLOCK.getId(newBlock.getBlock()))) {
|
|
||||||
LOGGER.debug("onBlockNew {} -> {}", oldBlock, newBlock);
|
|
||||||
Identifier blockId = Registries.BLOCK.getId(newBlock.getBlock());
|
|
||||||
PortalManager.onPortalBlockPlaced(world, pos, blockId);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (PORTAL_BLOCKS.contains(Registries.BLOCK.getId(oldBlock.getBlock()))) {
|
|
||||||
LOGGER.debug("onBlockOld {} -> {}", oldBlock, newBlock);
|
|
||||||
Identifier blockId = Registries.BLOCK.getId(oldBlock.getBlock());
|
|
||||||
PortalManager.onPortalBlockBroken(world, pos, blockId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package quimufu.colourful_portals.mixin;
|
|
||||||
|
|
||||||
import net.minecraft.client.texture.NativeImage;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
@Mixin(targets = "net/minecraft/client/texture/SpriteContents$Interpolation")
|
|
||||||
public abstract class InterpolationMixin {
|
|
||||||
|
|
||||||
@Shadow
|
|
||||||
protected abstract int lerp(double delta, int to, int from);
|
|
||||||
|
|
||||||
ThreadLocal<Integer> colourBefore = new ThreadLocal<>();
|
|
||||||
ThreadLocal<Double> delta = new ThreadLocal<>();
|
|
||||||
|
|
||||||
@Redirect(method = "apply", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/texture/NativeImage;setColor(III)V"))
|
|
||||||
private void injected(NativeImage instance, int x, int y, int color) {
|
|
||||||
int alpha = lerp(delta.get(), (color >> 24) & 0xFF, (colourBefore.get() >> 24) & 0xFF);
|
|
||||||
|
|
||||||
instance.setColor(x, y, (alpha << 24) | (color & 0xFFFFFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModifyVariable(method = "apply", at = @At("STORE"), ordinal = 10)
|
|
||||||
private int injected(int x) {
|
|
||||||
colourBefore.set(x);
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ModifyVariable(method = "apply", at = @At("STORE"), ordinal = 0)
|
|
||||||
private double injected(double deltaV) {
|
|
||||||
delta.set(deltaV);
|
|
||||||
return deltaV;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package quimufu.colourful_portals.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(ServerPlayNetworkHandler.class)
|
||||||
|
public interface ServerPlayNetworkHandlerAccessor {
|
||||||
|
@Accessor
|
||||||
|
double getLastTickX();
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
double getLastTickY();
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
double getLastTickZ();
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
double getLastTickRiddenX();
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
double getLastTickRiddenY();
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
double getLastTickRiddenZ();
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package quimufu.colourful_portals.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(ServerPlayerEntity.class)
|
||||||
|
public interface ServerPlayerEntityAccessor {
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
void setInTeleportationState(boolean inTeleportationState);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package quimufu.colourful_portals.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.server.world.ServerEntityManager;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.world.EntityList;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(ServerWorld.class)
|
||||||
|
public interface ServerWorldAccessor {
|
||||||
|
@Accessor
|
||||||
|
EntityList getEntityList();
|
||||||
|
}
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package quimufu.colourful_portals.mixin;
|
|
||||||
|
|
||||||
import me.jellysquid.mods.sodium.client.render.chunk.compile.buffers.ChunkModelBuilder;
|
|
||||||
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.FluidRenderer;
|
|
||||||
import net.minecraft.fluid.FluidState;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.BlockRenderView;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|
||||||
import quimufu.colourful_portals.client.SodiumPortalFluidRenderHandler;
|
|
||||||
|
|
||||||
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_FLUID;
|
|
||||||
|
|
||||||
@Mixin(FluidRenderer.class)
|
|
||||||
public class SodiumFluidRendererMixin {
|
|
||||||
|
|
||||||
private final SodiumPortalFluidRenderHandler sodiumPortalFluidRenderHandler = new SodiumPortalFluidRenderHandler();
|
|
||||||
|
|
||||||
@Inject(at = @At("HEAD"), method = "render", cancellable = true, remap = false)
|
|
||||||
private void init(BlockRenderView world, FluidState fluidState, BlockPos pos, BlockPos offset, ChunkModelBuilder buffers, CallbackInfoReturnable<Boolean> cir) {
|
|
||||||
if (fluidState.isOf(PORTAL_FLUID)) {
|
|
||||||
cir.setReturnValue(sodiumPortalFluidRenderHandler.render(world, fluidState, pos, offset, buffers));
|
|
||||||
cir.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
package quimufu.colourful_portals.portal;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.projectile.ArrowEntity;
|
||||||
|
import net.minecraft.entity.projectile.PersistentProjectileEntity;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.BlockBox;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
import net.minecraft.world.TeleportTarget;
|
||||||
|
import quimufu.colourful_portals.general_util.LinkedList;
|
||||||
|
import quimufu.colourful_portals.general_util.Node;
|
||||||
|
import quimufu.colourful_portals.mixin.ServerPlayNetworkHandlerAccessor;
|
||||||
|
import quimufu.colourful_portals.mixin.ServerPlayerEntityAccessor;
|
||||||
|
import quimufu.colourful_portals.util.RaycastHelper;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.MOD_ID;
|
||||||
|
import static quimufu.colourful_portals.portal.PortalHelper.*;
|
||||||
|
|
||||||
|
public class DefaultLinkingSystem implements PortalLinkingSystem {
|
||||||
|
|
||||||
|
public static final Identifier DEFAULT_LINKING_SYSTEM = Identifier.of(MOD_ID, "default_linking_system");
|
||||||
|
|
||||||
|
private final Map<BlockPos, Set<Node<PortalRepresentation>>> positionalLookup = new HashMap<>();
|
||||||
|
private final MinecraftServer server;
|
||||||
|
|
||||||
|
public DefaultLinkingSystem(MinecraftServer minecraftServer) {
|
||||||
|
this.server = minecraftServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Identifier getLinkingSystemId() {
|
||||||
|
return DEFAULT_LINKING_SYSTEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void linkPortals(LinkedList<PortalRepresentation> portalRepresentations) {
|
||||||
|
LOGGER.debug("start linkPortals");
|
||||||
|
for (Node<PortalRepresentation> node = portalRepresentations.getNode(0); node != null; node = node.getNext()) {
|
||||||
|
PortalRepresentation portalRepresentation = node.getValue();
|
||||||
|
|
||||||
|
Node<PortalRepresentation> finalNode = node;
|
||||||
|
insideOf(portalRepresentation)
|
||||||
|
.forEachRemaining(blockPos -> {
|
||||||
|
Set<Node<PortalRepresentation>> portalsAtPosition = positionalLookup.computeIfAbsent(blockPos, (k) -> new HashSet<>(1));
|
||||||
|
portalsAtPosition.add(finalNode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
LOGGER.debug("end linkPortals");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unLinkPortal(PortalRepresentation portalRepresentation) {
|
||||||
|
//no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean movementCallback(Entity entity, BlockPos pos, ServerWorld world) {
|
||||||
|
for (Direction.Axis axis : Direction.Axis.values()) {
|
||||||
|
Vec3d prevEntityPos;
|
||||||
|
if (entity instanceof ServerPlayerEntity serverPlayerEntity) {
|
||||||
|
ServerPlayNetworkHandlerAccessor spnha = (ServerPlayNetworkHandlerAccessor) serverPlayerEntity.networkHandler;
|
||||||
|
prevEntityPos = new Vec3d(spnha.getLastTickX(), spnha.getLastTickY(), spnha.getLastTickZ());
|
||||||
|
} else {
|
||||||
|
prevEntityPos = new Vec3d(entity.prevX, entity.prevY, entity.prevZ);
|
||||||
|
}
|
||||||
|
if (RaycastHelper.passedOnAxis(prevEntityPos, entity.getPos(), pos, axis)) {
|
||||||
|
if (onPortalPassed(entity, pos, world, axis)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a) {
|
||||||
|
Set<Node<PortalRepresentation>> portalRepresentationCandidates = positionalLookup.get(pos);
|
||||||
|
if (portalRepresentationCandidates == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Optional<Node<PortalRepresentation>> portalOpt = portalRepresentationCandidates.stream()
|
||||||
|
.filter(n -> !n.orphaned())
|
||||||
|
.filter(n -> n.getValue() != null)
|
||||||
|
.filter(n -> getAxisW(n.getValue()).rotateYClockwise().getAxis() == a)
|
||||||
|
.filter(n -> Objects.equals(n.getValue().dimensionId(), getDimId(world)))
|
||||||
|
.findAny();
|
||||||
|
if (portalOpt.isPresent()) {
|
||||||
|
Node<PortalRepresentation> fromNode = portalOpt.get();
|
||||||
|
Node<PortalRepresentation> toNode = fromNode.getNext() != null ? fromNode.getNext() : fromNode.getPartOf().getNode(0);
|
||||||
|
if (toNode == null || toNode.getValue() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PortalRepresentation fromPortal = fromNode.getValue();
|
||||||
|
PortalRepresentation toPortal = toNode.getValue();
|
||||||
|
BlockBox fromBox = fromPortal.location();
|
||||||
|
Vec3d fromCenter = new Vec3d((fromBox.getMaxX() + 1 + fromBox.getMinX()) / 2D, (fromBox.getMaxY() + 1 + fromBox.getMinY()) / 2D, (fromBox.getMaxZ() + 1 + fromBox.getMinZ()) / 2D);
|
||||||
|
BlockBox toBox = toPortal.location();
|
||||||
|
Vec3d toCenter = new Vec3d((toBox.getMaxX() + 1 + toBox.getMinX()) / 2D, (toBox.getMaxY() + 1 + toBox.getMinY()) / 2D, (toBox.getMaxZ() + 1 + toBox.getMinZ()) / 2D);
|
||||||
|
Vec3d relPos = entity.getPos().subtract(fromCenter);
|
||||||
|
Vec3d targetPos;
|
||||||
|
float targetYaw;
|
||||||
|
Vec3d targetVelocity;
|
||||||
|
ServerWorld toWorld = getPortalWorld(server, toPortal);
|
||||||
|
if (getAxisW(fromPortal) == getAxisW(toPortal)) {
|
||||||
|
targetPos = toCenter.add(relPos);
|
||||||
|
targetYaw = entity.getYaw();
|
||||||
|
targetVelocity = entity.getVelocity();
|
||||||
|
} else {
|
||||||
|
targetPos = toCenter.add(new Vec3d(-relPos.z, relPos.y, relPos.x));
|
||||||
|
if (entity instanceof PersistentProjectileEntity) {
|
||||||
|
// //un-fuck up arrow Yaw
|
||||||
|
// //rotate by 90°
|
||||||
|
// targetYaw = (entity.getYaw() + 90.F) % 360.F;
|
||||||
|
// //mirror
|
||||||
|
// targetYaw = (-targetYaw) % 360.F;
|
||||||
|
// //rotate by 90°
|
||||||
|
// targetYaw = (targetYaw - 90.F) % 360.F;
|
||||||
|
//
|
||||||
|
// //now it's normal!
|
||||||
|
// //Let's rotate it!
|
||||||
|
// targetYaw = (targetYaw + 90.F) % 360.F;
|
||||||
|
//
|
||||||
|
// //fuck it up again
|
||||||
|
// //rotate by 90°
|
||||||
|
// targetYaw = (targetYaw + 90.F) % 360.F;
|
||||||
|
// //mirror
|
||||||
|
// targetYaw = (-targetYaw) % 360.F;
|
||||||
|
// //rotate by 90°
|
||||||
|
// targetYaw = (targetYaw - 90.F) % 360.F;
|
||||||
|
//short:
|
||||||
|
targetYaw = (entity.getYaw() - 90.F) % 360.F;
|
||||||
|
} else {
|
||||||
|
targetYaw = (entity.getYaw() + 90.F) % 360.F;
|
||||||
|
}
|
||||||
|
Vec3d currVelocity = entity.getVelocity();
|
||||||
|
targetVelocity = new Vec3d(-currVelocity.z, currVelocity.y, currVelocity.x);
|
||||||
|
}
|
||||||
|
//todo: maybe fancy continoous movement math!
|
||||||
|
TeleportTarget teleportTarget = new TeleportTarget(toWorld, targetPos, targetVelocity, targetYaw, entity.getPitch(), TeleportTarget.ADD_PORTAL_CHUNK_TICKET);
|
||||||
|
if (entity instanceof ServerPlayerEntity) {
|
||||||
|
((ServerPlayerEntityAccessor) entity).setInTeleportationState(true);
|
||||||
|
}
|
||||||
|
Entity target;
|
||||||
|
if ((target = entity.teleportTo(teleportTarget)) != null) {
|
||||||
|
target.velocityDirty = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsReInit() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsMovementCallback() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServerWorld getPortalWorld(MinecraftServer server, PortalRepresentation fromPortalRepresentation) {
|
||||||
|
ServerWorld serverWorld = PortalHelper.getPortalWorld(server, fromPortalRepresentation);
|
||||||
|
if (serverWorld == null) {
|
||||||
|
LOGGER.warn("couldn't get portal dimensionId for portal {}. Don't sue me!", fromPortalRepresentation);
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
return serverWorld;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
package quimufu.colourful_portals.portal;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.registry.RegistryKey;
|
||||||
|
import net.minecraft.registry.RegistryKeys;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.TypeFilter;
|
||||||
|
import net.minecraft.util.math.*;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import qouteall.imm_ptl.core.api.PortalAPI;
|
||||||
|
import qouteall.imm_ptl.core.portal.Portal;
|
||||||
|
import qouteall.imm_ptl.core.portal.global_portals.GlobalPortalStorage;
|
||||||
|
import qouteall.q_misc_util.my_util.DQuaternion;
|
||||||
|
import quimufu.colourful_portals.general_util.LinkedList;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.MOD_ID;
|
||||||
|
|
||||||
|
public class ImmersivePortalsLinkingSystem implements PortalLinkingSystem {
|
||||||
|
|
||||||
|
public static final Identifier IMMERSIVE_PORTALS_LINKING_SYSTEM = Identifier.of(MOD_ID, "immersive_portals_linking_system");
|
||||||
|
|
||||||
|
private final MinecraftServer server;
|
||||||
|
|
||||||
|
public ImmersivePortalsLinkingSystem(MinecraftServer server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Identifier getLinkingSystemId() {
|
||||||
|
return IMMERSIVE_PORTALS_LINKING_SYSTEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assureLinked(PortalRepresentation fromPortalRepresentation, PortalRepresentation linkedToPortalRepresentation) {
|
||||||
|
|
||||||
|
ServerWorld fromPortalWorld = getPortalWorld(fromPortalRepresentation);
|
||||||
|
RegistryKey<World> linkedToPortalWorldRegKey = RegistryKey.of(RegistryKeys.WORLD, linkedToPortalRepresentation.dimensionId());
|
||||||
|
|
||||||
|
BlockBox fromPortalBlockBox = fromPortalRepresentation.location();
|
||||||
|
Box fromPortalBox = Box.from(fromPortalBlockBox);
|
||||||
|
BlockBox linkedToPortalBlockBox = linkedToPortalRepresentation.location();
|
||||||
|
Box linkedToPortalBox = Box.from(linkedToPortalBlockBox);
|
||||||
|
|
||||||
|
List<Portal> outgoingPortals = new ArrayList<>(getPortalList(fromPortalRepresentation));
|
||||||
|
|
||||||
|
if (outgoingPortals.size() > 2) {
|
||||||
|
LOGGER.warn("Found more then 2 portals in {}, cleaning up", fromPortalBox);
|
||||||
|
for (int i = 2; i < outgoingPortals.size(); i++) {
|
||||||
|
Portal outgoingPortal = outgoingPortals.get(i);
|
||||||
|
PortalAPI.removeGlobalPortal(getPortalWorld(fromPortalRepresentation), outgoingPortal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (outgoingPortals.isEmpty()) {
|
||||||
|
Portal portal = Portal.ENTITY_TYPE.create(fromPortalWorld);
|
||||||
|
if (portal == null) {
|
||||||
|
LOGGER.error("could not create PortalRepresentation entity for {}", fromPortalRepresentation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
portal.setOriginPos(fromPortalBox.getCenter());
|
||||||
|
portal.setDestinationDimension(linkedToPortalWorldRegKey);
|
||||||
|
portal.setDestination(linkedToPortalBox.getCenter());
|
||||||
|
Vec3d axisW = Vec3d.of(PortalHelper.getAxisW(fromPortalBlockBox).getVector());
|
||||||
|
portal.setOrientationAndSize(
|
||||||
|
axisW, // axisW
|
||||||
|
new Vec3d(0, 1, 0), // axisH
|
||||||
|
2, // width
|
||||||
|
3 // height
|
||||||
|
);
|
||||||
|
portal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
|
||||||
|
outgoingPortals.add(portal);
|
||||||
|
PortalAPI.addGlobalPortal(fromPortalWorld, portal);
|
||||||
|
}
|
||||||
|
if (outgoingPortals.size() == 1) {
|
||||||
|
Portal portal = PortalAPI.createFlippedPortal(outgoingPortals.getFirst());
|
||||||
|
outgoingPortals.add(portal);
|
||||||
|
PortalAPI.addGlobalPortal(fromPortalWorld, portal);
|
||||||
|
}
|
||||||
|
for (Portal outgoingPortal : outgoingPortals) {
|
||||||
|
outgoingPortal.setDestinationDimension(linkedToPortalWorldRegKey);
|
||||||
|
outgoingPortal.setDestination(linkedToPortalBox.getCenter());
|
||||||
|
Vec3d axisW = Vec3d.of(PortalHelper.getAxisW(fromPortalBlockBox).getVector());
|
||||||
|
outgoingPortal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
|
||||||
|
GlobalPortalStorage.get(getPortalWorld(fromPortalRepresentation)).onDataChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void linkPortals(LinkedList<PortalRepresentation> portalRepresentations) {
|
||||||
|
for (int i = 0; i < portalRepresentations.size(); i++) {
|
||||||
|
PortalRepresentation portalRepresentation = portalRepresentations.get(i);
|
||||||
|
PortalRepresentation linkedToPortalRepresentation = i + 1 < portalRepresentations.size() ? portalRepresentations.get(i + 1) : portalRepresentations.getFirst();
|
||||||
|
|
||||||
|
assureLinked(portalRepresentation, linkedToPortalRepresentation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unLinkPortal(PortalRepresentation portalRepresentation) {
|
||||||
|
List<Portal> portals = getPortalList(portalRepresentation);
|
||||||
|
portals.forEach(portalEntity -> PortalAPI.removeGlobalPortal(getPortalWorld(portalRepresentation), portalEntity));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a) {
|
||||||
|
//no-op, handled by immptl
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsReInit() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsMovementCallback() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean movementCallback(Entity entity, BlockPos p, ServerWorld world) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Portal> getPortalList(PortalRepresentation portalRepresentation) {
|
||||||
|
Box portalBox = Box.from(portalRepresentation.location());
|
||||||
|
getPortalWorld(portalRepresentation)
|
||||||
|
.getEntitiesByType(TypeFilter.instanceOf(Portal.class), portalBox, e -> e.isAlive() && contains(portalBox, e.getBoundingBox()))
|
||||||
|
.forEach(GlobalPortalStorage::convertNormalPortalIntoGlobalPortal);
|
||||||
|
return GlobalPortalStorage.get(getPortalWorld(portalRepresentation)).data.stream().filter(portal -> contains(portalBox, portal.getThinBoundingBox())).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean contains(Box outside, Box inside) {
|
||||||
|
return outside.contains(inside.getMaxPos()) && outside.contains(inside.getMinPos());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerWorld getPortalWorld(PortalRepresentation fromPortalRepresentation) {
|
||||||
|
ServerWorld serverWorld = PortalHelper.getPortalWorld(server, fromPortalRepresentation);
|
||||||
|
if (serverWorld == null) {
|
||||||
|
LOGGER.warn("couldn't get portal dimensionId for portal {}. Don't sue me!", fromPortalRepresentation);
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
return serverWorld;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,12 +3,14 @@ package quimufu.colourful_portals.portal;
|
|||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.registry.Registries;
|
import net.minecraft.registry.Registries;
|
||||||
|
import net.minecraft.registry.RegistryKey;
|
||||||
|
import net.minecraft.registry.RegistryKeys;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockBox;
|
import net.minecraft.util.math.*;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import net.minecraft.util.math.Direction;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import net.minecraft.util.math.Vec3i;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@@ -24,21 +26,21 @@ public class PortalHelper {
|
|||||||
{'N', 'X', 'X', 'N'},
|
{'N', 'X', 'X', 'N'},
|
||||||
};
|
};
|
||||||
|
|
||||||
static Iterator<BlockPos> insideOf(BlockBox portal) {
|
static Iterator<BlockPos> insideOf(PortalRepresentation portal) {
|
||||||
Direction axisW = getAxisW(portal);
|
BlockBox portal1 = portal.location();
|
||||||
|
Direction axisW = getAxisW(portal1);
|
||||||
Vec3i dir = axisW.getVector();
|
Vec3i dir = axisW.getVector();
|
||||||
BlockPos startPos = new BlockPos(portal.getMinX(), portal.getMinY(), portal.getMinZ());
|
BlockPos startPos = new BlockPos(portal1.getMinX(), portal1.getMinY(), portal1.getMinZ());
|
||||||
|
|
||||||
return new BlockPosIterator(startPos, dir, 'O');
|
return new BlockPosIterator(startPos, dir, 'O');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
static boolean isValidPortal(ServerWorld world, PortalRepresentation portal, Identifier blockId) {
|
||||||
static boolean isValidPortal(ServerWorld world, BlockBox portal, Identifier blockId) {
|
return isValidPortal(world, portal.location(), blockId, true, false);
|
||||||
return isValidPortal(world, portal, blockId, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isValidCandidate(ServerWorld world, BlockBox portal, Identifier blockId) {
|
static boolean isValidCandidate(ServerWorld world, PortalRepresentation portal, Identifier blockId) {
|
||||||
return isValidPortal(world, portal, blockId, false, false);
|
return isValidPortal(world, portal.location(), blockId, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isValidPortal(ServerWorld world, BlockBox portal, Identifier blockId, boolean empty, boolean placementCheck) {
|
private static boolean isValidPortal(ServerWorld world, BlockBox portal, Identifier blockId, boolean empty, boolean placementCheck) {
|
||||||
@@ -46,15 +48,19 @@ public class PortalHelper {
|
|||||||
return isValidPortal(world, Registries.BLOCK.get(blockId), new BlockPos(portal.getMinX(), portal.getMinY(), portal.getMinZ()), empty, axisW, placementCheck);
|
return isValidPortal(world, Registries.BLOCK.get(blockId), new BlockPos(portal.getMinX(), portal.getMinY(), portal.getMinZ()), empty, axisW, placementCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Direction getAxisW(PortalRepresentation portalRepresentation) {
|
||||||
|
return getAxisW(portalRepresentation.location());
|
||||||
|
}
|
||||||
|
|
||||||
static Direction getAxisW(BlockBox fromPortalBox) {
|
static Direction getAxisW(BlockBox fromPortalBox) {
|
||||||
if (fromPortalBox.getMaxX() - fromPortalBox.getMinX() > 2)
|
if (fromPortalBox.getMaxX() - fromPortalBox.getMinX() > 2)
|
||||||
return Direction.EAST;
|
return Direction.EAST;
|
||||||
return Direction.SOUTH;
|
return Direction.SOUTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<BlockBox> findPortalCandidates(ServerWorld world, BlockPos pos, Identifier blockId) {
|
static List<PortalRepresentation> findPortalCandidates(ServerWorld world, BlockPos pos, Identifier blockId) {
|
||||||
Block block = Registries.BLOCK.get(blockId);
|
Block block = Registries.BLOCK.get(blockId);
|
||||||
ArrayList<BlockBox> portals = new ArrayList<>();
|
ArrayList<PortalRepresentation> portals = new ArrayList<>();
|
||||||
outer:
|
outer:
|
||||||
for (Direction direction : Direction.values()) {
|
for (Direction direction : Direction.values()) {
|
||||||
int size = direction == Direction.UP || direction == Direction.DOWN ? 5 : 4;
|
int size = direction == Direction.UP || direction == Direction.DOWN ? 5 : 4;
|
||||||
@@ -164,17 +170,18 @@ public class PortalHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Optional<BlockBox> getPortalStartingAt(ServerWorld world, Block block, BlockPos lowerCorner, Direction direction) {
|
private static Optional<PortalRepresentation> getPortalStartingAt(ServerWorld world, Block block, BlockPos lowerCorner, Direction direction) {
|
||||||
Vec3i sidewardsOffset = direction.getVector().multiply(3);
|
Vec3i sidewardsOffset = direction.getVector().multiply(3);
|
||||||
Vec3i upwardsOffset = Direction.UP.getVector().multiply(4);
|
Vec3i upwardsOffset = Direction.UP.getVector().multiply(4);
|
||||||
if (isValidPortal(world, block, lowerCorner, false, direction, false)) {
|
if (isValidPortal(world, block, lowerCorner, false, direction, false)) {
|
||||||
return Optional.of(BlockBox.create(lowerCorner, lowerCorner.add(sidewardsOffset).add(upwardsOffset)));
|
BlockBox location = BlockBox.create(lowerCorner, lowerCorner.add(sidewardsOffset).add(upwardsOffset));
|
||||||
|
return Optional.of(new PortalRepresentation(location, getDimId(world)));
|
||||||
}
|
}
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isPortalPlaceable(ServerWorld world, BlockBox portal, Identifier blockId) {
|
public static boolean isPortalPlaceable(ServerWorld world, PortalRepresentation portal, Identifier blockId) {
|
||||||
return isValidPortal(world, portal, blockId, false, true);
|
return isValidPortal(world, portal.location(), blockId, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isValidPortal(ServerWorld world, Block block, BlockPos lowerCorner, boolean hasToBeFilledCorrectly, Direction direction, boolean placementCheck) {
|
private static boolean isValidPortal(ServerWorld world, Block block, BlockPos lowerCorner, boolean hasToBeFilledCorrectly, Direction direction, boolean placementCheck) {
|
||||||
@@ -212,6 +219,19 @@ public class PortalHelper {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static ServerWorld getPortalWorld(MinecraftServer server, PortalRepresentation fromPortalRepresentation) {
|
||||||
|
return server.getWorld(RegistryKey.of(RegistryKeys.WORLD, fromPortalRepresentation.dimensionId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Identifier getDimId(ServerWorld world) {
|
||||||
|
return world.getDimensionEntry().getKey().orElseThrow().getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull Iterable<BlockPos> blockPosInBox(Box boundinBox) {
|
||||||
|
return BlockPos.iterate(MathHelper.floor(boundinBox.minX), MathHelper.floor(boundinBox.minY), MathHelper.floor(boundinBox.minZ), MathHelper.floor(boundinBox.maxX), MathHelper.floor(boundinBox.maxY), MathHelper.floor(boundinBox.maxZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class BlockPosIterator implements Iterator<BlockPos> {
|
private static class BlockPosIterator implements Iterator<BlockPos> {
|
||||||
private final BlockPos startPos;
|
private final BlockPos startPos;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package quimufu.colourful_portals.portal;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import quimufu.colourful_portals.general_util.LinkedList;
|
||||||
|
|
||||||
|
public interface PortalLinkingSystem {
|
||||||
|
Identifier getLinkingSystemId();
|
||||||
|
|
||||||
|
void linkPortals(LinkedList<PortalRepresentation> portalRepresentations);
|
||||||
|
|
||||||
|
void unLinkPortal(PortalRepresentation portalRepresentation);
|
||||||
|
|
||||||
|
boolean onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a);
|
||||||
|
|
||||||
|
boolean needsReInit();
|
||||||
|
|
||||||
|
boolean needsMovementCallback();
|
||||||
|
|
||||||
|
boolean movementCallback(Entity entity, BlockPos p, ServerWorld world);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package quimufu.colourful_portals.portal;
|
||||||
|
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface PortalLinkingSystemBuilder {
|
||||||
|
|
||||||
|
PortalLinkingSystem build(MinecraftServer minecraftServer);
|
||||||
|
}
|
||||||
@@ -1,67 +1,110 @@
|
|||||||
package quimufu.colourful_portals.portal;
|
package quimufu.colourful_portals.portal;
|
||||||
|
|
||||||
import dev.onyxstudios.cca.api.v3.component.Component;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.nbt.NbtElement;
|
import net.minecraft.nbt.NbtElement;
|
||||||
import net.minecraft.nbt.NbtHelper;
|
import net.minecraft.nbt.NbtHelper;
|
||||||
import net.minecraft.nbt.NbtList;
|
import net.minecraft.nbt.NbtList;
|
||||||
|
import net.minecraft.registry.RegistryWrapper;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.math.BlockBox;
|
import net.minecraft.util.math.BlockBox;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.WorldProperties;
|
import org.ladysnake.cca.api.v3.component.Component;
|
||||||
import quimufu.colourful_portals.util.Pair;
|
import quimufu.colourful_portals.general_util.LinkedList;
|
||||||
|
import quimufu.colourful_portals.general_util.Node;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
|
||||||
|
|
||||||
public class PortalListComponent implements Component {
|
public class PortalListComponent implements Component {
|
||||||
private final WorldProperties worldProperties;
|
public static final int CURRENT_VERSION = 1;
|
||||||
HashMap<Identifier, List<Pair<BlockBox, Identifier>>> portalsPerPortalBlock = new HashMap<>();
|
HashMap<Identifier, LinkedList<PortalRepresentation>> portalsPerPortalBlock = new HashMap<>();
|
||||||
|
|
||||||
public PortalListComponent(WorldProperties worldProperties) {
|
Identifier lastPortalLinkingSystem = Identifier.of(MOD_ID, "immersive_portals_linking_system");
|
||||||
this.worldProperties = worldProperties;
|
|
||||||
|
public PortalListComponent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readFromNbt(NbtCompound tag) {
|
public void readFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
|
||||||
if (tag != null && !tag.isEmpty()) {
|
if (tag != null && !tag.isEmpty()) {
|
||||||
for (String block : tag.getKeys()) {
|
if (!tag.contains("version")) {
|
||||||
NbtList portalsWithDim = tag.getList(block, NbtElement.COMPOUND_TYPE);
|
readFromOldNbt(tag);
|
||||||
ArrayList<Pair<BlockBox, Identifier>> portals = new ArrayList<>();
|
}
|
||||||
|
if (tag.getInt("version") > CURRENT_VERSION) {
|
||||||
|
LOGGER.warn("portals component comes from a newer version! Hopefully backwards compatible. Proceeding");
|
||||||
|
}
|
||||||
|
if (tag.contains("linking_system")) {
|
||||||
|
lastPortalLinkingSystem = Identifier.tryParse(tag.getString("linking_system"));
|
||||||
|
}
|
||||||
|
NbtCompound blocks = tag.getCompound("blocks");
|
||||||
|
for (String block : blocks.getKeys()) {
|
||||||
|
NbtList portalsWithDim = blocks.getList(block, NbtElement.COMPOUND_TYPE);
|
||||||
|
LinkedList<PortalRepresentation> portalRepresentations = new LinkedList<>();
|
||||||
for (int i = 0; i < portalsWithDim.size(); i++) {
|
for (int i = 0; i < portalsWithDim.size(); i++) {
|
||||||
NbtCompound portalWithDim = portalsWithDim.getCompound(i);
|
NbtCompound portalWithDim = portalsWithDim.getCompound(i);
|
||||||
|
|
||||||
NbtCompound portalCompound = portalWithDim.getCompound("portal");
|
NbtCompound portalCompound = portalWithDim.getCompound("portal");
|
||||||
BlockBox portal = BlockBox.create(NbtHelper.toBlockPos(portalCompound.getCompound("from")),
|
BlockBox portal = BlockBox.create(NbtHelper.toBlockPos(portalCompound, "from").orElseThrow(),
|
||||||
NbtHelper.toBlockPos(portalCompound.getCompound("to")));
|
NbtHelper.toBlockPos(portalCompound, "to").orElseThrow());
|
||||||
|
|
||||||
Identifier dimension = Identifier.tryParse(portalWithDim.getString("dim"));
|
Identifier dimension = Identifier.tryParse(portalWithDim.getString("dim"));
|
||||||
|
|
||||||
portals.add(Pair.of(portal, dimension));
|
portalRepresentations.add(new PortalRepresentation(portal, dimension));
|
||||||
}
|
}
|
||||||
portalsPerPortalBlock.put(Identifier.tryParse(block), portals);
|
portalsPerPortalBlock.put(Identifier.tryParse(block), portalRepresentations);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void readFromOldNbt(NbtCompound tag) {
|
||||||
|
for (String block : tag.getKeys()) {
|
||||||
|
NbtList portalsWithDim = tag.getList(block, NbtElement.COMPOUND_TYPE);
|
||||||
|
LinkedList<PortalRepresentation> portalRepresentations = new LinkedList<>();
|
||||||
|
for (int i = 0; i < portalsWithDim.size(); i++) {
|
||||||
|
NbtCompound portalWithDim = portalsWithDim.getCompound(i);
|
||||||
|
|
||||||
|
NbtCompound portalCompound = portalWithDim.getCompound("portal");
|
||||||
|
BlockBox portal = BlockBox.create(toBlockPosOld(portalCompound.getCompound("from")),
|
||||||
|
toBlockPosOld(portalCompound.getCompound("to")));
|
||||||
|
|
||||||
|
Identifier dimension = Identifier.tryParse(portalWithDim.getString("dim"));
|
||||||
|
|
||||||
|
portalRepresentations.add(new PortalRepresentation(portal, dimension));
|
||||||
|
}
|
||||||
|
portalsPerPortalBlock.put(Identifier.tryParse(block), portalRepresentations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockPos toBlockPosOld(NbtCompound nbt) {
|
||||||
|
return new BlockPos(nbt.getInt("X"), nbt.getInt("Y"), nbt.getInt("Z"));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeToNbt(NbtCompound tag) {
|
public void writeToNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
|
||||||
|
LOGGER.debug("save");
|
||||||
|
if(PORTAL_MANAGER == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tag.putInt("version", CURRENT_VERSION);
|
||||||
|
tag.putString("linking_system", PORTAL_MANAGER.getLinkingSystem().getLinkingSystemId().toString());
|
||||||
|
NbtCompound blocks = new NbtCompound();
|
||||||
|
tag.put("blocks", blocks);
|
||||||
for (Identifier portalBlockId : portalsPerPortalBlock.keySet()) {
|
for (Identifier portalBlockId : portalsPerPortalBlock.keySet()) {
|
||||||
NbtList portalsWithDimList = new NbtList();
|
NbtList portalsWithDimList = new NbtList();
|
||||||
tag.put(portalBlockId.toString(), portalsWithDimList);
|
blocks.put(portalBlockId.toString(), portalsWithDimList);
|
||||||
for (Pair<BlockBox, Identifier> portalWithDim : portalsPerPortalBlock.get(portalBlockId)) {
|
for (PortalRepresentation portalRepresentationWithDim : portalsPerPortalBlock.get(portalBlockId)) {
|
||||||
NbtCompound portalWithDimCompound = new NbtCompound();
|
NbtCompound portalWithDimCompound = new NbtCompound();
|
||||||
portalsWithDimList.add(portalWithDimCompound);
|
portalsWithDimList.add(portalWithDimCompound);
|
||||||
|
|
||||||
NbtCompound portalCompound = new NbtCompound();
|
NbtCompound portalCompound = new NbtCompound();
|
||||||
portalWithDimCompound.put("portal", portalCompound);
|
portalWithDimCompound.put("portal", portalCompound);
|
||||||
portalWithDimCompound.putString("dim", portalWithDim.second.toString());
|
portalWithDimCompound.putString("dim", portalRepresentationWithDim.dimensionId().toString());
|
||||||
|
|
||||||
BlockBox portal = portalWithDim.first;
|
BlockBox portal = portalRepresentationWithDim.location();
|
||||||
BlockPos from = new BlockPos(portal.getMinX(), portal.getMinY(), portal.getMinZ());
|
BlockPos from = new BlockPos(portal.getMinX(), portal.getMinY(), portal.getMinZ());
|
||||||
BlockPos to = new BlockPos(portal.getMaxX(), portal.getMaxY(), portal.getMaxZ());
|
BlockPos to = new BlockPos(portal.getMaxX(), portal.getMaxY(), portal.getMaxZ());
|
||||||
|
|
||||||
@@ -70,59 +113,54 @@ public class PortalListComponent implements Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
LOGGER.debug("portals {}", tag.toString());
|
LOGGER.debug("portals {}", tag);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BlockBox> getContainingPortals(Identifier blockId, BlockPos pos, Identifier dim) {
|
public List<PortalRepresentation> getContainingPortals(Identifier blockId, BlockPos pos, Identifier dim) {
|
||||||
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>()).stream()
|
return getPortals(blockId).stream()
|
||||||
.filter((portalWithDim) -> (portalWithDim.second.equals(dim)
|
.filter((portal) -> (portal.dimensionId().equals(dim) && portal.location().contains(pos)))
|
||||||
&& portalWithDim.first.contains(pos)))
|
|
||||||
.map((p) -> p.first)
|
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createPortal(Identifier blockId, Pair<BlockBox, Identifier> portalWithDim) {
|
public void createPortal(Identifier blockId, PortalRepresentation portal) {
|
||||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
LinkedList<PortalRepresentation> portalRepresentations = getPortals(blockId);
|
||||||
if (!portals.contains(portalWithDim)) {
|
if (!portalRepresentations.contains(portal)) {
|
||||||
portals.add(portalWithDim);
|
portalRepresentations.add(portal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removePortal(Identifier blockId, Pair<BlockBox, Identifier> portalWithDim) {
|
public void removePortal(Identifier blockId, PortalRepresentation portal) {
|
||||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
getPortals(blockId).remove(portal);
|
||||||
portals.remove(portalWithDim);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Pair<BlockBox, Identifier>> getPortals(Identifier blockId) {
|
public LinkedList<PortalRepresentation> getPortals(Identifier blockId) {
|
||||||
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new LinkedList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Set<Identifier> getBlockIds() {
|
public Set<Identifier> getBlockIds() {
|
||||||
return portalsPerPortalBlock.keySet();
|
return portalsPerPortalBlock.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<BlockBox, Identifier> getNext(Identifier blockId, BlockBox portal, Identifier dim) {
|
public PortalRepresentation getNext(Identifier blockId, PortalRepresentation portalRepresentationWithDim) {
|
||||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
LinkedList<PortalRepresentation> portals = getPortals(blockId);
|
||||||
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal, dim);
|
Node<PortalRepresentation> node;
|
||||||
|
if ((node = portals.getNodeOf(portalRepresentationWithDim)) == null) {
|
||||||
int nextIndex = portals.indexOf(portalWithDim) + 1;
|
return portals.getFirst();
|
||||||
if (nextIndex >= portals.size()) {
|
}
|
||||||
return portals.get(0);
|
return node.getNext() == null ? portals.getFirst() : node.getNext().getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
return portals.get(nextIndex);
|
public boolean containsPortal(Identifier blockId, PortalRepresentation portalRepresentation) {
|
||||||
|
return getPortals(blockId).contains(portalRepresentation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsPortal(Identifier blockId, Pair<BlockBox,Identifier> portal) {
|
public PortalRepresentation getLast(Identifier blockId) {
|
||||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
return getPortals(blockId).getLast();
|
||||||
return portals.contains(portal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<BlockBox, Identifier> getLast(Identifier blockId) {
|
public Identifier lastPortalLinkingSystemId() {
|
||||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
return lastPortalLinkingSystem;
|
||||||
return portals.get(portals.size()-1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,106 +3,106 @@ package quimufu.colourful_portals.portal;
|
|||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.registry.RegistryKey;
|
|
||||||
import net.minecraft.registry.RegistryKeys;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.world.ServerWorld;
|
import net.minecraft.server.world.ServerWorld;
|
||||||
import net.minecraft.util.DyeColor;
|
import net.minecraft.util.DyeColor;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.TypeFilter;
|
import net.minecraft.util.math.BlockBox;
|
||||||
import net.minecraft.util.math.*;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.util.math.Direction;
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import qouteall.imm_ptl.core.api.PortalAPI;
|
|
||||||
import qouteall.imm_ptl.core.portal.Portal;
|
|
||||||
import qouteall.q_misc_util.my_util.DQuaternion;
|
|
||||||
import quimufu.colourful_portals.config.ColourfulPortalConfig;
|
import quimufu.colourful_portals.config.ColourfulPortalConfig;
|
||||||
import quimufu.colourful_portals.util.Pair;
|
import quimufu.colourful_portals.general_util.LinkedList;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
|
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
|
||||||
import static quimufu.colourful_portals.Components.PORTAL_CANDIDATE_LIST;
|
|
||||||
import static quimufu.colourful_portals.Components.PORTAL_LIST;
|
|
||||||
|
|
||||||
public class PortalManager {
|
public class PortalManager {
|
||||||
|
|
||||||
public static void onPortalBlockPlaced(ServerWorld world, BlockPos pos, Identifier blockId) {
|
private final PortalListComponent portalCandidateList;
|
||||||
|
private final PortalListComponent portalList;
|
||||||
|
|
||||||
|
public final AtomicBoolean blockUpdatesPending = new AtomicBoolean(false);
|
||||||
|
public final Queue<Runnable> pendingUpdates = new java.util.LinkedList<>();
|
||||||
|
|
||||||
|
public PortalLinkingSystem getLinkingSystem() {
|
||||||
|
return linkingSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final PortalLinkingSystem linkingSystem;
|
||||||
|
|
||||||
|
public PortalManager(PortalLinkingSystem portalLinkingSystem, PortalListComponent portalCandidateList, PortalListComponent portalList) {
|
||||||
|
this.portalCandidateList = portalCandidateList;
|
||||||
|
this.portalList = portalList;
|
||||||
|
this.linkingSystem = portalLinkingSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPortalBlockPlaced(ServerWorld world, BlockPos pos, Identifier blockId) {
|
||||||
world.getProfiler().push("onPortalBlockPlaced");
|
world.getProfiler().push("onPortalBlockPlaced");
|
||||||
LOGGER.debug("onPortalBlockPlaced, {}", blockId);
|
LOGGER.debug("onPortalBlockPlaced, {}", blockId);
|
||||||
|
|
||||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
Identifier dimIdentifier = PortalHelper.getDimId(world);
|
||||||
|
|
||||||
//delete portalCandidates obstructed by PortalBlocks for consistency
|
//delete portalCandidates obstructed by PortalBlocks for consistency
|
||||||
List<BlockBox> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, world.getDimensionKey().getValue());
|
List<PortalRepresentation> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dimIdentifier);
|
||||||
LOGGER.debug("got containing PortalCandidates , {}", portalCandidates);
|
LOGGER.debug("got containing PortalCandidates , {}", portalCandidates);
|
||||||
for (BlockBox portalCandidate : portalCandidates) {
|
for (PortalRepresentation portalCandidate : portalCandidates) {
|
||||||
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
|
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
|
||||||
LOGGER.debug("invalid, {}", portalCandidate);
|
LOGGER.debug("invalid, {}", portalCandidate);
|
||||||
Pair<BlockBox, Identifier> portalWithDim =
|
|
||||||
Pair.of(portalCandidate, world.getDimensionKey().getValue());
|
|
||||||
|
|
||||||
portalCandidateList.removePortal(blockId, portalWithDim);
|
portalCandidateList.removePortal(blockId, portalCandidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//find new portalCandidates created by PortalBlock placement
|
//find new portalCandidates created by PortalBlock placement
|
||||||
List<BlockBox> portals = PortalHelper.findPortalCandidates(world, pos, blockId);
|
List<PortalRepresentation> portals = PortalHelper.findPortalCandidates(world, pos, blockId);
|
||||||
LOGGER.debug("new portalCandidates found, {}", portals);
|
LOGGER.debug("new portalCandidates found, {}", portals);
|
||||||
|
|
||||||
for (BlockBox portal : portals) {
|
for (PortalRepresentation portal : portals) {
|
||||||
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal, world.getDimensionKey().getValue());
|
portalCandidateList.createPortal(blockId, portal);
|
||||||
portalCandidateList.createPortal(blockId, portalWithDim);
|
|
||||||
}
|
}
|
||||||
world.getProfiler().pop();
|
world.getProfiler().pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void onPortalBlockBroken(ServerWorld world, BlockPos pos, Identifier blockId) {
|
public void onPortalBlockBroken(ServerWorld world, BlockPos pos, Identifier blockId) {
|
||||||
world.getProfiler().push("onPortalBlockBroken");
|
world.getProfiler().push("onPortalBlockBroken");
|
||||||
LOGGER.debug("onPortalBlockBroken, {}", blockId);
|
LOGGER.debug("onPortalBlockBroken, {}", blockId);
|
||||||
|
|
||||||
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
|
|
||||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
|
||||||
|
|
||||||
//check portalCandidate validity
|
//check portalCandidate validity
|
||||||
List<BlockBox> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, world.getDimensionKey().getValue());
|
Identifier dimensionIdentifier = PortalHelper.getDimId(world);
|
||||||
|
List<PortalRepresentation> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dimensionIdentifier);
|
||||||
LOGGER.debug("getContainingPortals, {}", portalCandidates);
|
LOGGER.debug("getContainingPortals, {}", portalCandidates);
|
||||||
for (BlockBox portalCandidate : portalCandidates) {
|
for (PortalRepresentation portalCandidate : portalCandidates) {
|
||||||
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
|
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
|
||||||
LOGGER.debug("invalid, {}", portalCandidate);
|
LOGGER.debug("invalid, {}", portalCandidate);
|
||||||
Pair<BlockBox, Identifier> portalWithDim =
|
portalCandidateList.removePortal(blockId, portalCandidate);
|
||||||
Pair.of(portalCandidate, world.getDimensionKey().getValue());
|
|
||||||
portalCandidateList.removePortal(blockId, portalWithDim);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//add portalCandidates deobstructed by PortalBlock removal
|
//add portalCandidates deobstructed by PortalBlock removal
|
||||||
for (Direction direction : Direction.values()) {
|
for (Direction direction : Direction.values()) {
|
||||||
List<BlockBox> newPortalCandidates = PortalHelper.findPortalCandidates(world, pos.add(direction.getVector()), blockId);
|
List<PortalRepresentation> newPortalCandidates = PortalHelper.findPortalCandidates(world, pos.add(direction.getVector()), blockId);
|
||||||
|
|
||||||
for (BlockBox newPortal : newPortalCandidates) {
|
for (PortalRepresentation newPortal : newPortalCandidates) {
|
||||||
LOGGER.debug("potentially new candidate after deobstruction , {}", newPortal);
|
LOGGER.debug("potentially new candidate after deobstruction , {}", newPortal);
|
||||||
|
portalCandidateList.createPortal(blockId, newPortal);
|
||||||
PortalListComponent portalListComponent = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
|
||||||
Pair<BlockBox, Identifier> portalWithDim =
|
|
||||||
Pair.of(newPortal, world.getDimensionKey().getValue());
|
|
||||||
portalListComponent.createPortal(blockId, portalWithDim);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//check portal validity
|
//check portal validity
|
||||||
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, world.getDimensionKey().getValue());
|
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dimensionIdentifier);
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
for (BlockBox portal : portals) {
|
for (PortalRepresentation portal : portals) {
|
||||||
if (!PortalHelper.isValidPortal(world, portal, blockId)) {
|
if (!PortalHelper.isValidPortal(world, portal, blockId)) {
|
||||||
LOGGER.debug("portal became invalid ,{} {}", world.getDimensionKey().getValue(), portal);
|
LOGGER.debug("portal became invalid ,{} {}", dimensionIdentifier, portal);
|
||||||
|
|
||||||
Pair<BlockBox, Identifier> portalWithDim =
|
portalList.removePortal(blockId, portal);
|
||||||
Pair.of(portal, world.getDimensionKey().getValue());
|
linkingSystem.unLinkPortal(portal);
|
||||||
portalList.removePortal(blockId, portalWithDim);
|
removePortalBlocks(world, portal.location());
|
||||||
destroyPortalEntitiesInside(world, Box.from(portal));
|
|
||||||
removePortalBlocks(world, portal);
|
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,49 +114,47 @@ public class PortalManager {
|
|||||||
world.getProfiler().pop();
|
world.getProfiler().pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void removePortalBlocks(ServerWorld world, BlockBox portal) {
|
private void removePortalBlocks(ServerWorld world, BlockBox portal) {
|
||||||
BlockPos.stream(portal)
|
BlockPos.stream(portal)
|
||||||
.filter(blockPos -> world.getBlockState(blockPos).isOf(PORTAL_BLOCK))
|
.filter(blockPos -> world.getBlockState(blockPos).isOf(PORTAL_BLOCK))
|
||||||
.forEach(blockPos -> world.setBlockState(blockPos, Blocks.AIR.getDefaultState()));
|
.forEach(blockPos -> world.setBlockState(blockPos, Blocks.AIR.getDefaultState()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void fixPortalGroup(Identifier blockId, PortalListComponent portalList, MinecraftServer server) {
|
private void fixPortalGroup(Identifier blockId, PortalListComponent portalList, MinecraftServer server) {
|
||||||
List<Pair<BlockBox, Identifier>> portalsWithDim = portalList.getPortals(blockId);
|
LinkedList<PortalRepresentation> portalsWithDim = portalList.getPortals(blockId);
|
||||||
if (portalsWithDim.size() == 1) {
|
if (portalsWithDim.size() == 1) {
|
||||||
Pair<BlockBox, Identifier> portal = portalsWithDim.get(0);
|
PortalRepresentation portalRepresentation = portalsWithDim.getFirst();
|
||||||
ServerWorld world = getPortalWorld(portal, server);
|
ServerWorld world = PortalHelper.getPortalWorld(server, portalRepresentation);
|
||||||
if (world == null) {
|
if (world == null) {
|
||||||
LOGGER.error("error fixing portalGroup, world {} was null", portal.second);
|
LOGGER.error("error fixing portalGroup, dimensionId {} was null", portalRepresentation.dimensionId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal.first, portal.second);
|
portalList.removePortal(blockId, portalRepresentation);
|
||||||
portalList.removePortal(blockId, portalWithDim);
|
linkingSystem.unLinkPortal(portalRepresentation);
|
||||||
destroyPortalEntitiesInside(world, Box.from(portal.first));
|
removePortalBlocks(world, portalRepresentation.location());
|
||||||
removePortalBlocks(world, portal.first);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < portalsWithDim.size(); i++) {
|
for (PortalRepresentation portalRepresentation : portalsWithDim) {
|
||||||
Pair<BlockBox, Identifier> portal = portalsWithDim.get(i);
|
assurePortalBlocksPlaced(blockId, portalRepresentation, server);
|
||||||
Pair<BlockBox, Identifier> linkedToPortal = i + 1 < portalsWithDim.size() ? portalsWithDim.get(i + 1) : portalsWithDim.get(0);
|
|
||||||
assureLinkedTo(portal, linkedToPortal, server);
|
|
||||||
assurePortalBlocksPlaced(blockId, portal, server);
|
|
||||||
}
|
}
|
||||||
|
linkingSystem.linkPortals(portalsWithDim);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assurePortalBlocksPlaced(Identifier blockId, Pair<BlockBox, Identifier> portal, MinecraftServer server) {
|
private void assurePortalBlocksPlaced(Identifier blockId, PortalRepresentation portalRepresentation, MinecraftServer server) {
|
||||||
DyeColor color = ColourfulPortalConfig.portalBlocks.get(blockId.toString());
|
DyeColor color = ColourfulPortalConfig.colorOf(blockId.toString());
|
||||||
if (color != null) {
|
if (color != null) {
|
||||||
Direction.Axis portalPlaneAxis = PortalHelper.getAxisW(portal.first)
|
Direction.Axis portalPlaneAxis = PortalHelper.getAxisW(portalRepresentation)
|
||||||
.rotateClockwise(Direction.Axis.Y)
|
.rotateClockwise(Direction.Axis.Y)
|
||||||
.getAxis();
|
.getAxis();
|
||||||
ServerWorld portalWorld = getPortalWorld(portal, server);
|
ServerWorld portalWorld = PortalHelper.getPortalWorld(server, portalRepresentation);
|
||||||
if (portalWorld == null) {
|
if (portalWorld == null) {
|
||||||
LOGGER.error("error placing portal planes, world {} was null", portal.second);
|
LOGGER.error("error placing portalRepresentation planes, dimensionId {} was null", portalRepresentation.dimensionId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BlockState portalBlockState = PORTAL_BLOCK.getStateWith(color, portalPlaneAxis);
|
BlockState portalBlockState = PORTAL_BLOCK.getStateWith(color, portalPlaneAxis);
|
||||||
|
|
||||||
PortalHelper.insideOf(portal.first)
|
PortalHelper.insideOf(portalRepresentation)
|
||||||
.forEachRemaining(blockPos -> {
|
.forEachRemaining(blockPos -> {
|
||||||
BlockState blockState = portalWorld.getBlockState(blockPos);
|
BlockState blockState = portalWorld.getBlockState(blockPos);
|
||||||
if (blockState.isAir() || blockState.getFluidState().isOf(PORTAL_FLUID)) {
|
if (blockState.isAir() || blockState.getFluidState().isOf(PORTAL_FLUID)) {
|
||||||
@@ -170,99 +168,22 @@ public class PortalManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assureLinkedTo(Pair<BlockBox, Identifier> fromPortal, Pair<BlockBox, Identifier> linkedToPortal, MinecraftServer server) {
|
|
||||||
|
|
||||||
ServerWorld fromPortalWorld = getPortalWorld(fromPortal, server);
|
public boolean tryIgnite(ServerWorld world, BlockPos pos) {
|
||||||
RegistryKey<World> linkedToPortalWorldRegKey = RegistryKey.of(RegistryKeys.WORLD, linkedToPortal.second);
|
|
||||||
|
|
||||||
BlockBox fromPortalBlockBox = fromPortal.first;
|
|
||||||
Box fromPortalBox = Box.from(fromPortalBlockBox);
|
|
||||||
BlockBox linkedToPortalBlockBox = linkedToPortal.first;
|
|
||||||
Box linkedToPortalBox = Box.from(linkedToPortalBlockBox);
|
|
||||||
if (fromPortalWorld == null) {
|
|
||||||
LOGGER.error("error linking portals, world {} was null", fromPortalBlockBox);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Portal> portals = fromPortalWorld.getEntitiesByType(TypeFilter.instanceOf(Portal.class), fromPortalBox, Entity::isAlive);
|
|
||||||
List<Portal> outgoingPortals = new ArrayList<>();
|
|
||||||
for (Portal portal : portals) {
|
|
||||||
Box portalBoundingBox = portal.getBoundingBox();
|
|
||||||
//is portal actually inside portalBox?
|
|
||||||
if (fromPortalBox.contains(portalBoundingBox.minX, portalBoundingBox.minY, portalBoundingBox.minZ)
|
|
||||||
&& fromPortalBox.contains(portalBoundingBox.maxX, portalBoundingBox.maxY, portalBoundingBox.maxZ)) {
|
|
||||||
outgoingPortals.add(portal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (outgoingPortals.size() > 2) {
|
|
||||||
LOGGER.warn("Found more then 2 portals in {}, cleaning up", fromPortalBox);
|
|
||||||
for (int i = 2; i < outgoingPortals.size(); i++) {
|
|
||||||
Portal outgoingPortal = outgoingPortals.get(i);
|
|
||||||
outgoingPortal.kill();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (outgoingPortals.isEmpty()) {
|
|
||||||
Portal portal = Portal.entityType.create(fromPortalWorld);
|
|
||||||
if (portal == null) {
|
|
||||||
LOGGER.error("could not create Portal entity for {}", fromPortal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
portal.setOriginPos(fromPortalBox.getCenter());
|
|
||||||
portal.setDestinationDimension(linkedToPortalWorldRegKey);
|
|
||||||
portal.setDestination(linkedToPortalBox.getCenter());
|
|
||||||
Vec3d axisW = Vec3d.of(PortalHelper.getAxisW(fromPortalBlockBox).getVector());
|
|
||||||
portal.setOrientationAndSize(
|
|
||||||
axisW, // axisW
|
|
||||||
new Vec3d(0, 1, 0), // axisH
|
|
||||||
2, // width
|
|
||||||
3 // height
|
|
||||||
);
|
|
||||||
portal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
|
|
||||||
outgoingPortals.add(portal);
|
|
||||||
PortalAPI.spawnServerEntity(portal);
|
|
||||||
}
|
|
||||||
if (outgoingPortals.size() == 1) {
|
|
||||||
Portal portal = PortalAPI.createFlippedPortal(outgoingPortals.get(0));
|
|
||||||
outgoingPortals.add(portal);
|
|
||||||
PortalAPI.spawnServerEntity(portal);
|
|
||||||
}
|
|
||||||
for (Portal outgoingPortal : outgoingPortals) {
|
|
||||||
outgoingPortal.setDestinationDimension(linkedToPortalWorldRegKey);
|
|
||||||
outgoingPortal.setDestination(linkedToPortalBox.getCenter());
|
|
||||||
Vec3d axisW = Vec3d.of(PortalHelper.getAxisW(fromPortalBlockBox).getVector());
|
|
||||||
outgoingPortal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
|
|
||||||
outgoingPortal.reloadAndSyncToClient();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private static ServerWorld getPortalWorld(Pair<BlockBox, Identifier> fromPortal, MinecraftServer server) {
|
|
||||||
return server.getWorld(RegistryKey.of(RegistryKeys.WORLD, fromPortal.second));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void destroyPortalEntitiesInside(ServerWorld world, Box box) {
|
|
||||||
List<Portal> portals = world.getEntitiesByType(TypeFilter.instanceOf(Portal.class), box, Entity::isAlive);
|
|
||||||
portals.forEach(portal -> PortalAPI.removeGlobalPortal(world, portal));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean tryIgnite(ServerWorld world, BlockPos pos) {
|
|
||||||
world.getProfiler().push("portal ignition");
|
world.getProfiler().push("portal ignition");
|
||||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
LOGGER.info("portal ignition");
|
||||||
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
|
|
||||||
Set<Identifier> blockIds = portalCandidateList.getBlockIds();
|
Set<Identifier> blockIds = portalCandidateList.getBlockIds();
|
||||||
boolean ret = false;
|
boolean ret = false;
|
||||||
for (Identifier blockId : blockIds) {
|
for (Identifier blockId : blockIds) {
|
||||||
Identifier dim = world.getDimensionKey().getValue();
|
Identifier dim = PortalHelper.getDimId(world);
|
||||||
List<BlockBox> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dim);
|
List<PortalRepresentation> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dim);
|
||||||
for (BlockBox portalCandidate : portalCandidates) {
|
for (PortalRepresentation current : portalCandidates) {
|
||||||
if (PortalHelper.isValidPortal(world, portalCandidate, blockId)) {
|
if (PortalHelper.isValidPortal(world, current, blockId)) {
|
||||||
Pair<BlockBox, Identifier> current = Pair.of(portalCandidate, dim);
|
PortalRepresentation next = portalCandidateList.getNext(blockId, current);
|
||||||
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, portalCandidate, dim);
|
ServerWorld nextWorld = PortalHelper.getPortalWorld(world.getServer(), next);
|
||||||
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
|
|
||||||
if (!next.equals(current)
|
if (!next.equals(current)
|
||||||
&& (!portalList.containsPortal(blockId, current) || !portalList.containsPortal(blockId, next))
|
&& (!portalList.containsPortal(blockId, current) || !portalList.containsPortal(blockId, next))
|
||||||
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)) {
|
&& PortalHelper.isPortalPlaceable(nextWorld, next, blockId)) {
|
||||||
portalList.createPortal(blockId, current);
|
portalList.createPortal(blockId, current);
|
||||||
portalList.createPortal(blockId, next);
|
portalList.createPortal(blockId, next);
|
||||||
ret = true;
|
ret = true;
|
||||||
@@ -275,24 +196,21 @@ public class PortalManager {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean canExtend(ServerWorld world, BlockPos pos) {
|
public boolean canExtend(ServerWorld world, BlockPos pos) {
|
||||||
world.getProfiler().push("portal extension check");
|
world.getProfiler().push("portal extension check");
|
||||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
|
||||||
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
|
|
||||||
Set<Identifier> blockIds = portalList.getBlockIds();
|
Set<Identifier> blockIds = portalList.getBlockIds();
|
||||||
Identifier dim = world.getDimensionKey().getValue();
|
Identifier dim = PortalHelper.getDimId(world);
|
||||||
|
|
||||||
for (Identifier blockId : blockIds) {
|
for (Identifier blockId : blockIds) {
|
||||||
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, dim);
|
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dim);
|
||||||
for (BlockBox portal : portals) {
|
for (PortalRepresentation portal : portals) {
|
||||||
if (PortalHelper.isValidPortal(world, portal, blockId)) {
|
if (PortalHelper.isValidPortal(world, portal, blockId)) {
|
||||||
Pair<BlockBox, Identifier> last = portalList.getLast(blockId);
|
PortalRepresentation last = portalList.getLast(blockId);
|
||||||
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, last.first, last.second);
|
PortalRepresentation next = portalCandidateList.getNext(blockId, last);
|
||||||
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
|
ServerWorld nextWorld = PortalHelper.getPortalWorld(world.getServer(), next);
|
||||||
|
|
||||||
if (!portalList.containsPortal(blockId, next)
|
if (!portalList.containsPortal(blockId, next)
|
||||||
&& PortalHelper.isValidPortal(world, portal, blockId)
|
&& PortalHelper.isPortalPlaceable(nextWorld, next, blockId)) {
|
||||||
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)){
|
|
||||||
world.getProfiler().pop();
|
world.getProfiler().pop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -305,25 +223,22 @@ public class PortalManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean extend(ServerWorld world, BlockPos pos) {
|
public boolean extend(ServerWorld world, BlockPos pos) {
|
||||||
world.getProfiler().push("portal extension");
|
world.getProfiler().push("portal extension");
|
||||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
|
||||||
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
|
|
||||||
Set<Identifier> blockIds = portalList.getBlockIds();
|
Set<Identifier> blockIds = portalList.getBlockIds();
|
||||||
Identifier dim = world.getDimensionKey().getValue();
|
Identifier dim = PortalHelper.getDimId(world);
|
||||||
|
|
||||||
boolean ret = false;
|
boolean ret = false;
|
||||||
for (Identifier blockId : blockIds) {
|
for (Identifier blockId : blockIds) {
|
||||||
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, dim);
|
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dim);
|
||||||
for (BlockBox portal : portals) {
|
for (PortalRepresentation portal : portals) {
|
||||||
if (PortalHelper.isValidPortal(world, portal, blockId)) {
|
if (PortalHelper.isValidPortal(world, portal, blockId)) {
|
||||||
Pair<BlockBox, Identifier> last = portalList.getLast(blockId);
|
PortalRepresentation last = portalList.getLast(blockId);
|
||||||
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, last.first, last.second);
|
PortalRepresentation next = portalCandidateList.getNext(blockId, last);
|
||||||
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
|
ServerWorld nextWorld = PortalHelper.getPortalWorld(world.getServer(), next);
|
||||||
|
|
||||||
if (!portalList.containsPortal(blockId, next)
|
if (!portalList.containsPortal(blockId, next)
|
||||||
&& PortalHelper.isValidPortal(world, portal, blockId)
|
&& PortalHelper.isPortalPlaceable(nextWorld, next, blockId)) {
|
||||||
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)){
|
|
||||||
portalList.createPortal(blockId, next);
|
portalList.createPortal(blockId, next);
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
@@ -336,4 +251,49 @@ public class PortalManager {
|
|||||||
world.getProfiler().pop();
|
world.getProfiler().pop();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a) {
|
||||||
|
linkingSystem.onPortalPassed(entity, pos, world, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onMovementThroughBlock(Entity entity, BlockPos pos, ServerWorld world) {
|
||||||
|
return linkingSystem.movementCallback(entity, pos, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onLoad(MinecraftServer minecraftServer) {
|
||||||
|
HashSet<Identifier> blockIdsToDelete = new HashSet<>(portalCandidateList.getBlockIds());
|
||||||
|
blockIdsToDelete.addAll(portalList.getBlockIds());
|
||||||
|
blockIdsToDelete.removeAll(PORTAL_BLOCKS);
|
||||||
|
for (Identifier blockId : blockIdsToDelete) {
|
||||||
|
for (PortalRepresentation portal : portalCandidateList.getPortals(blockId)) {
|
||||||
|
portalCandidateList.removePortal(blockId, portal);
|
||||||
|
}
|
||||||
|
for (PortalRepresentation portal : portalList.getPortals(blockId)) {
|
||||||
|
portalList.removePortal(blockId, portal);
|
||||||
|
linkingSystem.unLinkPortal(portal);
|
||||||
|
ServerWorld portalWorld = PortalHelper.getPortalWorld(minecraftServer, portal);
|
||||||
|
if (portalWorld != null) {
|
||||||
|
removePortalBlocks(portalWorld, portal.location());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runPendingUpdates() {
|
||||||
|
if(pendingUpdates.size()<20){
|
||||||
|
blockUpdatesPending.set(false);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < Math.min(20, pendingUpdates.size()); i++) {
|
||||||
|
Runnable runnable;
|
||||||
|
synchronized (pendingUpdates) {
|
||||||
|
runnable = pendingUpdates.poll();
|
||||||
|
}
|
||||||
|
if(runnable == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package quimufu.colourful_portals.portal;
|
||||||
|
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.BlockBox;
|
||||||
|
|
||||||
|
public record PortalRepresentation(BlockBox location, Identifier dimensionId) {
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package quimufu.colourful_portals.portal;
|
||||||
|
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
|
public record PrioritizedPortalLinkingSystemBuilder(PortalLinkingSystemBuilder portalLinkingSystemBuilder,
|
||||||
|
IntSupplier priority) {}
|
||||||
@@ -13,7 +13,6 @@ public enum NullableAxis implements StringIdentifiable {
|
|||||||
private final Direction.Axis axis;
|
private final Direction.Axis axis;
|
||||||
|
|
||||||
NullableAxis(Direction.Axis axis) {
|
NullableAxis(Direction.Axis axis) {
|
||||||
|
|
||||||
this.axis = axis;
|
this.axis = axis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import quimufu.colourful_portals.portal.PortalManager;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
||||||
|
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_MANAGER;
|
||||||
|
|
||||||
public class PortalFluid extends Fluid {
|
public class PortalFluid extends Fluid {
|
||||||
public static final EnumProperty<NullableAxis> AXIS = EnumProperty.of("axis", NullableAxis.class);
|
public static final EnumProperty<NullableAxis> AXIS = EnumProperty.of("axis", NullableAxis.class);
|
||||||
@@ -138,7 +139,7 @@ public class PortalFluid extends Fluid {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (amount <= 14) {
|
if (amount <= 14) {
|
||||||
//*might* change my state in world, returned value is what new state should be
|
//*might* change my state in dimensionId, returned value is what new state should be
|
||||||
newStateMe = steal(world, pos, newStateMe, currentAxis);
|
newStateMe = steal(world, pos, newStateMe, currentAxis);
|
||||||
}
|
}
|
||||||
//my state might have changed.
|
//my state might have changed.
|
||||||
@@ -169,7 +170,7 @@ public class PortalFluid extends Fluid {
|
|||||||
} else if (!world.isClient) {
|
} else if (!world.isClient) {
|
||||||
for (Direction direction : Direction.values()) {
|
for (Direction direction : Direction.values()) {
|
||||||
if (ColourfulPortalsMod.PORTAL_BLOCKS.contains(Registries.BLOCK.getId(world.getBlockState(pos.offset(direction)).getBlock()))
|
if (ColourfulPortalsMod.PORTAL_BLOCKS.contains(Registries.BLOCK.getId(world.getBlockState(pos.offset(direction)).getBlock()))
|
||||||
&& PortalManager.tryIgnite((ServerWorld) world, pos)) {
|
&& PORTAL_MANAGER.tryIgnite((ServerWorld) world, pos)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,6 +198,8 @@ public class PortalFluid extends Fluid {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Direction candidate = null;
|
Direction candidate = null;
|
||||||
|
BlockState candidateBlockState = null;
|
||||||
|
|
||||||
for (Direction dir : Direction.values()) {
|
for (Direction dir : Direction.values()) {
|
||||||
if (dir == targetDir.getOpposite() || dir == targetDir) {
|
if (dir == targetDir.getOpposite() || dir == targetDir) {
|
||||||
continue;
|
continue;
|
||||||
@@ -211,12 +214,16 @@ public class PortalFluid extends Fluid {
|
|||||||
//if adjacent to any solid Block, it's a candidate
|
//if adjacent to any solid Block, it's a candidate
|
||||||
if (neighbourOfInspected.isSideSolidFullSquare(world, neighborOfInspectedLocation, dir.getOpposite())) {
|
if (neighbourOfInspected.isSideSolidFullSquare(world, neighborOfInspectedLocation, dir.getOpposite())) {
|
||||||
candidate = dir;
|
candidate = dir;
|
||||||
|
candidateBlockState = world.getBlockState(pos.offset(dir));
|
||||||
|
|
||||||
}
|
}
|
||||||
if (candidate == null && !neighbourOfInspected.isReplaceable()) {
|
if ((candidate == null || candidateBlockState.isReplaceable()) && !neighbourOfInspected.isReplaceable()) {
|
||||||
candidate = dir;
|
candidate = dir;
|
||||||
|
candidateBlockState = world.getBlockState(pos.offset(dir));
|
||||||
}
|
}
|
||||||
if (candidate == null && !neighbourOfInspected.isReplaceable()) {
|
if (candidate == null) {
|
||||||
candidate = dir;
|
candidate = dir;
|
||||||
|
candidateBlockState = world.getBlockState(pos.offset(dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -372,7 +379,7 @@ public class PortalFluid extends Fluid {
|
|||||||
BlockPos neighborOfInspectedLocation = inspectedLocation.offset(dir);
|
BlockPos neighborOfInspectedLocation = inspectedLocation.offset(dir);
|
||||||
BlockState neighbourOfInspected = world.getBlockState(neighborOfInspectedLocation);
|
BlockState neighbourOfInspected = world.getBlockState(neighborOfInspectedLocation);
|
||||||
|
|
||||||
//if adjacent to a Portal Block, 2 less
|
//if adjacent to a PortalRepresentation Block, 2 less
|
||||||
if (ColourfulPortalsMod.PORTAL_BLOCKS.contains(Registries.BLOCK.getId(neighbourOfInspected.getBlock()))) {
|
if (ColourfulPortalsMod.PORTAL_BLOCKS.contains(Registries.BLOCK.getId(neighbourOfInspected.getBlock()))) {
|
||||||
dirCost -= 2;
|
dirCost -= 2;
|
||||||
discount = false;
|
discount = false;
|
||||||
@@ -381,7 +388,7 @@ public class PortalFluid extends Fluid {
|
|||||||
//if adjacent to any solid Block, 1 less
|
//if adjacent to any solid Block, 1 less
|
||||||
if (neighbourOfInspected.isSideSolidFullSquare(world, neighborOfInspectedLocation, dir.getOpposite())) {
|
if (neighbourOfInspected.isSideSolidFullSquare(world, neighborOfInspectedLocation, dir.getOpposite())) {
|
||||||
discount = true;
|
discount = true;
|
||||||
//maybe a Portal Block in another direction
|
//maybe a PortalRepresentation Block in another direction
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package quimufu.colourful_portals.portal_fluid;
|
|||||||
|
|
||||||
import net.minecraft.block.*;
|
import net.minecraft.block.*;
|
||||||
import net.minecraft.entity.ai.pathing.NavigationType;
|
import net.minecraft.entity.ai.pathing.NavigationType;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.fluid.FluidState;
|
import net.minecraft.fluid.FluidState;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.item.Items;
|
||||||
@@ -18,6 +19,7 @@ import net.minecraft.util.shape.VoxelShapes;
|
|||||||
import net.minecraft.world.BlockView;
|
import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.WorldAccess;
|
import net.minecraft.world.WorldAccess;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -106,7 +108,7 @@ public class PortalFluidBlock
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
|
protected boolean canPathfindThrough(BlockState state, NavigationType type) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +118,7 @@ public class PortalFluidBlock
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ItemStack tryDrainFluid(WorldAccess world, BlockPos pos, BlockState state) {
|
public ItemStack tryDrainFluid(PlayerEntity player, WorldAccess world, BlockPos pos, BlockState state) {
|
||||||
if (state.getFluidState().get(PortalFluid.AMOUNT) == 16) {
|
if (state.getFluidState().get(PortalFluid.AMOUNT) == 16) {
|
||||||
world.setBlockState(pos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL | Block.REDRAW_ON_MAIN_THREAD);
|
world.setBlockState(pos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL | Block.REDRAW_ON_MAIN_THREAD);
|
||||||
return new ItemStack(this.fluid.getBucketItem());
|
return new ItemStack(this.fluid.getBucketItem());
|
||||||
@@ -126,6 +128,7 @@ public class PortalFluidBlock
|
|||||||
return ItemStack.EMPTY;
|
return ItemStack.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Optional<SoundEvent> getBucketFillSound() {
|
public Optional<SoundEvent> getBucketFillSound() {
|
||||||
return fluid.getBucketFillSound();
|
return fluid.getBucketFillSound();
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ public class PortalFluidBucketItem extends BucketItem {
|
|||||||
BlockPos pos = blockHitResult.getBlockPos();
|
BlockPos pos = blockHitResult.getBlockPos();
|
||||||
BlockState blockState = world.getBlockState(pos);
|
BlockState blockState = world.getBlockState(pos);
|
||||||
Block block = blockState.getBlock();
|
Block block = blockState.getBlock();
|
||||||
if (block instanceof FluidFillable && ((FluidFillable) block).canFillWithFluid(world, pos, blockState, fluid)) {
|
if (block instanceof FluidFillable && ((FluidFillable) block).canFillWithFluid(user, world, pos, blockState, fluid)) {
|
||||||
if (placeFluid(user, world, pos, blockHitResult)) {
|
if (placeFluid(user, world, pos, blockHitResult)) {
|
||||||
return TypedActionResult.success(BucketItem.getEmptiedStack(itemStack, user), world.isClient());
|
return TypedActionResult.success(BucketItem.getEmptiedStack(itemStack, user), world.isClient());
|
||||||
}
|
}
|
||||||
@@ -63,7 +63,7 @@ public class PortalFluidBucketItem extends BucketItem {
|
|||||||
Block block = blockState.getBlock();
|
Block block = blockState.getBlock();
|
||||||
boolean shouldTryPlace = blockState.isAir()
|
boolean shouldTryPlace = blockState.isAir()
|
||||||
|| blockState.canBucketPlace(this.fluid)
|
|| blockState.canBucketPlace(this.fluid)
|
||||||
|| block instanceof FluidFillable && ((FluidFillable) block).canFillWithFluid(world, pos, blockState, this.fluid);
|
|| block instanceof FluidFillable && ((FluidFillable) block).canFillWithFluid(player, world, pos, blockState, this.fluid);
|
||||||
if (!shouldTryPlace) {
|
if (!shouldTryPlace) {
|
||||||
return hitResult != null && this.placeFluid(player, world, hitResult.getBlockPos().offset(hitResult.getSide()), null);
|
return hitResult != null && this.placeFluid(player, world, hitResult.getBlockPos().offset(hitResult.getSide()), null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package quimufu.colourful_portals.util;
|
||||||
|
|
||||||
|
import static java.lang.Float.NaN;
|
||||||
|
|
||||||
|
public class AdditionalMath {
|
||||||
|
|
||||||
|
public static float root(float x, int nThRoot){
|
||||||
|
if(nThRoot<=0){
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
if(x<0 && nThRoot%2 == 1){
|
||||||
|
return -(float) Math.pow(-x, 1D / nThRoot);
|
||||||
|
}
|
||||||
|
return (float) Math.pow(x, 1D / nThRoot);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package quimufu.colourful_portals.util;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class Pair<A, B> {
|
|
||||||
|
|
||||||
public final A first;
|
|
||||||
public final B second;
|
|
||||||
|
|
||||||
public Pair(A first, B second) {
|
|
||||||
this.first = first;
|
|
||||||
this.second = second;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <A, B> Pair<A, B> of(A first, B second) {
|
|
||||||
return new Pair<>(first, second);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Pair<?, ?> pair = (Pair<?, ?>) o;
|
|
||||||
return Objects.equals(first, pair.first) && Objects.equals(second, pair.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(first, second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
20
src/main/java/quimufu/colourful_portals/util/Procedure.java
Normal file
20
src/main/java/quimufu/colourful_portals/util/Procedure.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package quimufu.colourful_portals.util;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Procedure {
|
||||||
|
void run();
|
||||||
|
|
||||||
|
default Procedure andThen(Procedure after){
|
||||||
|
return () -> {
|
||||||
|
this.run();
|
||||||
|
after.run();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default Procedure compose(Procedure before){
|
||||||
|
return () -> {
|
||||||
|
before.run();
|
||||||
|
this.run();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package quimufu.colourful_portals.util;
|
||||||
|
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RaycastHelper {
|
||||||
|
|
||||||
|
|
||||||
|
public static List<BlockPos> passedBlocks(Vec3d from, Vec3d to) {
|
||||||
|
LinkedList<BlockPos> psitions = new LinkedList<>();
|
||||||
|
Vec3d difference = to.subtract(from);
|
||||||
|
if(to.equals(from)){
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
double[] diff = new double[]{difference.getX(), difference.getY(), difference.getZ()};
|
||||||
|
double[] start = new double[]{from.getX(), from.getY(), from.getZ()};
|
||||||
|
double[] pos = new double[]{from.getX(), from.getY(), from.getZ()};
|
||||||
|
BlockPos currentBlockPos = BlockPos.ofFloored(pos[0], pos[1], pos[2]);
|
||||||
|
|
||||||
|
double currDelta = 0;
|
||||||
|
|
||||||
|
while (currDelta < 1) {
|
||||||
|
psitions.add(currentBlockPos);
|
||||||
|
double minMissing = 1;
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if (diff[i] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double curr;
|
||||||
|
if ((curr = ((round(pos[i], Math.signum(diff[i])) - pos[i]) / diff[i])) < minMissing) {
|
||||||
|
minMissing = curr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currDelta += minMissing;
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
pos[i] = start[i] + currDelta * diff[i];
|
||||||
|
}
|
||||||
|
currentBlockPos = BlockPos.ofFloored(pos[0], pos[1], pos[2]);
|
||||||
|
}
|
||||||
|
BlockPos last = BlockPos.ofFloored(to);
|
||||||
|
if(!psitions.getLast().equals(last)){
|
||||||
|
psitions.add(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
return psitions;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double round(double val, double signum) {
|
||||||
|
if (signum > 0) {
|
||||||
|
return Math.floor(val + signum);
|
||||||
|
}
|
||||||
|
return Math.ceil(val + signum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean passedOnAxis(Vec3d from, Vec3d to, BlockPos blockPos, Direction.Axis axis) {
|
||||||
|
double startAA = from.getComponentAlongAxis(axis);
|
||||||
|
double centerAA = blockPos.toCenterPos().getComponentAlongAxis(axis);
|
||||||
|
Vec3d diff = to.subtract(from);
|
||||||
|
double differenceAA = diff.getComponentAlongAxis(axis);
|
||||||
|
|
||||||
|
double hitAfter = (centerAA - startAA) / differenceAA;
|
||||||
|
|
||||||
|
if (differenceAA == 0 || hitAfter > 1 || hitAfter < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BlockPos.ofFloored(from.add(diff.multiply(hitAfter)))
|
||||||
|
.equals(blockPos);
|
||||||
|
}
|
||||||
|
}
|
||||||
358
src/main/java/quimufu/colourful_portals/util/TeleportHelper.java
Normal file
358
src/main/java/quimufu/colourful_portals/util/TeleportHelper.java
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
package quimufu.colourful_portals.util;
|
||||||
|
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.mob.MobEntity;
|
||||||
|
import net.minecraft.registry.Registries;
|
||||||
|
import net.minecraft.registry.RegistryKey;
|
||||||
|
import net.minecraft.registry.RegistryKeys;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.ServerTask;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.server.world.ChunkTicketType;
|
||||||
|
import net.minecraft.server.world.ServerWorld;
|
||||||
|
import net.minecraft.sound.SoundCategory;
|
||||||
|
import net.minecraft.sound.SoundEvents;
|
||||||
|
import net.minecraft.util.Identifier;
|
||||||
|
import net.minecraft.util.math.*;
|
||||||
|
import net.minecraft.world.Heightmap;
|
||||||
|
import net.minecraft.world.TeleportTarget;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import net.minecraft.world.border.WorldBorder;
|
||||||
|
import net.minecraft.world.chunk.Chunk;
|
||||||
|
import net.minecraft.world.dimension.DimensionType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.joml.Random;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||||
|
import quimufu.colourful_portals.config.ColourfulPortalConfig;
|
||||||
|
import quimufu.colourful_portals.mixin.ServerWorldAccessor;
|
||||||
|
import quimufu.colourful_portals.portal.PortalHelper;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class TeleportHelper extends ServerTask {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TeleportHelper.class);
|
||||||
|
public static final int TICK_DELAY = 2;
|
||||||
|
private final Random random;
|
||||||
|
private final MinecraftServer minecraftServer;
|
||||||
|
private final ConcurrentSkipListSet<TeleportRequest> toTeleport = new ConcurrentSkipListSet<>();
|
||||||
|
private static TeleportHelper INSTANCE;
|
||||||
|
private final AtomicBoolean tickScheduled = new AtomicBoolean(false);
|
||||||
|
private int lastExecTick;
|
||||||
|
private final ScheduledExecutorService sleeperExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||||
|
|
||||||
|
private static TeleportHelper getInstance(MinecraftServer server) {
|
||||||
|
if (INSTANCE == null) {
|
||||||
|
INSTANCE = new TeleportHelper(server);
|
||||||
|
}
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TeleportHelper(MinecraftServer server) {
|
||||||
|
super(server.getTicks(), null);
|
||||||
|
this.minecraftServer = server;
|
||||||
|
this.random = new Random();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean targetValid(Entity entity, ServerWorld world, Vector3f targetPos, boolean force) {
|
||||||
|
Box boundingBox = entity.getDimensions(entity.getPose())
|
||||||
|
.getBoxAt(new Vec3d(targetPos))
|
||||||
|
.stretch(0, -1, 0);
|
||||||
|
boolean foundAir = false;
|
||||||
|
boolean foundNonAir = false;
|
||||||
|
for (BlockPos pos : PortalHelper.blockPosInBox(boundingBox)) {
|
||||||
|
BlockState blockState = world.getBlockState(pos);
|
||||||
|
if (!blockState.isIn(ColourfulPortalsMod.COLOURFUL_PEARL_REPLACEABLE_BLOCK_TAG)) {
|
||||||
|
ColourfulPortalsMod.LOGGER.info("invalid location at {}, block {}", pos, Registries.BLOCK.getId(blockState.getBlock()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (blockState.isAir()) {
|
||||||
|
foundAir = true;
|
||||||
|
} else {
|
||||||
|
foundNonAir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (foundNonAir) && (foundAir || force);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable TeleportTarget getTarget(Entity entity, ServerWorld serverWorld, Vector3f from, ServerWorld targetWorld, BlockPos targetBlockPos) {
|
||||||
|
minecraftServer.getProfiler().push("getChunk");
|
||||||
|
Chunk chunk = targetWorld.getChunk(targetBlockPos);
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
|
||||||
|
if (isEmptyPosition(targetWorld, targetBlockPos, chunk)) {
|
||||||
|
log.info("empty at {}, retrying", targetBlockPos);
|
||||||
|
TeleportHelper.markForTeleport(entity, from, serverWorld);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Vector3f targetPos = Vec3d.of(targetBlockPos).toVector3f();
|
||||||
|
int width = MathHelper.ceil(entity.getBoundingBox().getLengthX());
|
||||||
|
int depth = MathHelper.ceil(entity.getBoundingBox().getLengthZ());
|
||||||
|
targetPos.add((width % 2) * 0.5F, 0, (depth % 2) * 0.5F);
|
||||||
|
minecraftServer.getProfiler().push("valid check");
|
||||||
|
int tries = 0;
|
||||||
|
while (!targetValid(entity, targetWorld, targetPos, tries > targetWorld.getLogicalHeight() * 2)) {
|
||||||
|
if (tries > targetWorld.getLogicalHeight() * 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
targetPos.y = getTargetY(targetWorld);
|
||||||
|
tries++;
|
||||||
|
}
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
return new TeleportTarget(targetWorld, new Vec3d(targetPos), entity.getVelocity(), entity.getYaw(), entity.getPitch(), TeleportTarget.NO_OP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isEmptyPosition(ServerWorld world, BlockPos target, Chunk chunk) {
|
||||||
|
minecraftServer.getProfiler().push("sampleHeightmap");
|
||||||
|
int height = chunk.sampleHeightmap(Heightmap.Type.WORLD_SURFACE, target.getX() & 0xF, target.getZ() & 0xF) + 1;
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
return height == world.getBottomY();
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Nullable ServerWorld getTargetWorld(ServerWorld serverWorld) {
|
||||||
|
if (random.nextFloat() < ColourfulPortalConfig.pearlSameDimensionLikelihood) {
|
||||||
|
return serverWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftServer server = serverWorld.getServer();
|
||||||
|
ColourfulPortalConfig.addMissingDimensionsToConfig(server);
|
||||||
|
Identifier targetWorldId = ColourfulPortalsMod.DIMENSION_WEIGHTS_COLOURFUL_PEARL.getWeighted(random);
|
||||||
|
|
||||||
|
return server.getWorld(RegistryKey.of(RegistryKeys.WORLD, targetWorldId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockPos getTargetPos(ServerWorld fromWorld, ServerWorld toWorld, Vector3f pos) {
|
||||||
|
|
||||||
|
double distance = ColourfulPortalConfig.minPearlDistance +
|
||||||
|
random.nextFloat() *
|
||||||
|
(ColourfulPortalConfig.maxPearlDistance - ColourfulPortalConfig.minPearlDistance);
|
||||||
|
|
||||||
|
double angle = Math.PI * 2 * random.nextFloat();
|
||||||
|
int targetY = getTargetY(toWorld);
|
||||||
|
|
||||||
|
Vec3d target = new Vec3d(pos.x + Math.cos(angle) * distance, targetY, pos.z + Math.sin(angle) * distance);
|
||||||
|
|
||||||
|
WorldBorder worldBorder = toWorld.getWorldBorder();
|
||||||
|
double d = DimensionType.getCoordinateScaleFactor(fromWorld.getDimension(), toWorld.getDimension());
|
||||||
|
return worldBorder.clamp(target.getX() * d, target.getY(), target.getZ() * d);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getTargetY(ServerWorld toWorld) {
|
||||||
|
return toWorld.getBottomY() + 4 + (random.nextInt(toWorld.getLogicalHeight() - 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void tryTeleport(Entity entity, Vector3f from, ServerWorld serverWorld, BlockPos to, ServerWorld toWorld) {
|
||||||
|
if (entity == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (entity.hasVehicle()) {
|
||||||
|
entity.detach();
|
||||||
|
}
|
||||||
|
minecraftServer.getProfiler().push("getTarget");
|
||||||
|
TeleportTarget target = getTarget(entity, serverWorld, from, toWorld, to);
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
if (target == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prepareTarget(target, entity);
|
||||||
|
((ServerWorldAccessor) serverWorld).getEntityList()
|
||||||
|
.add(entity);
|
||||||
|
|
||||||
|
minecraftServer.getProfiler().push("teleportTo");
|
||||||
|
if (entity instanceof ServerPlayerEntity serverPlayerEntity) {
|
||||||
|
if (serverPlayerEntity.networkHandler.isConnectionOpen()) {
|
||||||
|
|
||||||
|
Entity entityAfterTeleport = entity.teleportTo(target);
|
||||||
|
if (entityAfterTeleport == null) {
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entityAfterTeleport.onLanding();
|
||||||
|
serverPlayerEntity.clearCurrentExplosion();
|
||||||
|
entityAfterTeleport.damage(entityAfterTeleport.getDamageSources().fall(), 5.0f);
|
||||||
|
serverPlayerEntity.playSoundToPlayer(SoundEvents.ENTITY_PLAYER_TELEPORT, SoundCategory.PLAYERS, 1F, 0.75F);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Entity entityAfterTeleport = entity.teleportTo(target);
|
||||||
|
if (entityAfterTeleport == null) {
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
entityAfterTeleport.onLanding();
|
||||||
|
serverWorld.playSound(entityAfterTeleport, entityAfterTeleport.getBlockPos(),
|
||||||
|
SoundEvents.ENTITY_PLAYER_TELEPORT, SoundCategory.NEUTRAL,
|
||||||
|
1F, 0.75F);
|
||||||
|
}
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void prepareTarget(TeleportTarget target, Entity entity) {
|
||||||
|
Box entityBody = entity.getDimensions(entity.getPose()).getBoxAt(target.pos());
|
||||||
|
double minY = entityBody.getMinPos().getY();
|
||||||
|
Box floor = entityBody.withMinY(minY - 1).withMaxY(minY - 0.5);
|
||||||
|
for (BlockPos pos : PortalHelper.blockPosInBox(entityBody)) {
|
||||||
|
target.world().setBlockState(pos, ColourfulPortalsMod.COLOURFUL_AIR.getRandomState(random));
|
||||||
|
}
|
||||||
|
for (BlockPos pos : PortalHelper.blockPosInBox(floor)) {
|
||||||
|
target.world().setBlockState(pos, Blocks.STONE.getDefaultState());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean canTeleportEntityTo(Entity entity, World world) {
|
||||||
|
if (entity.getWorld().getRegistryKey() == world.getRegistryKey()) {
|
||||||
|
if (entity instanceof LivingEntity livingEntity) {
|
||||||
|
return livingEntity.isAlive() && !livingEntity.isSleeping();
|
||||||
|
}
|
||||||
|
return entity.isAlive();
|
||||||
|
}
|
||||||
|
return entity.canUsePortals(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void addTeleportRequest(Entity entity, Vector3f from,
|
||||||
|
ServerWorld fromWorld,
|
||||||
|
boolean originalInvulnerability) {
|
||||||
|
ServerWorld targetWorld = getTargetWorld(fromWorld);
|
||||||
|
if (targetWorld == null) {
|
||||||
|
log.info("couldn't find target world");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BlockPos targetBlockPos = getTargetPos(fromWorld, targetWorld, from);
|
||||||
|
if (entity instanceof MobEntity mobEntity
|
||||||
|
&& !mobEntity.isPersistent()
|
||||||
|
&& !mobEntity.cannotDespawn()
|
||||||
|
&& mobEntity.canImmediatelyDespawn(targetBlockPos.getSquaredDistance(new Vec3d(from)))
|
||||||
|
&& !targetWorld.isChunkLoaded(targetBlockPos)) {
|
||||||
|
log.info("despawning {} instead of teleporting it OOB", mobEntity.getName().getString());
|
||||||
|
|
||||||
|
((ServerWorldAccessor) fromWorld).getEntityList().add(entity);
|
||||||
|
mobEntity.discard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!canTeleportEntityTo(entity, targetWorld)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
targetWorld.getChunkManager()
|
||||||
|
.addTicket(ChunkTicketType.PORTAL, new ChunkPos(targetBlockPos), 2, targetBlockPos);
|
||||||
|
toTeleport.add(new TeleportRequest(entity.getUuid(),
|
||||||
|
from,
|
||||||
|
fromWorld.getRegistryKey().getValue(),
|
||||||
|
originalInvulnerability,
|
||||||
|
targetBlockPos,
|
||||||
|
targetWorld.getRegistryKey().getValue()
|
||||||
|
));
|
||||||
|
|
||||||
|
if (!tickScheduled.getAndSet(true)) {
|
||||||
|
long nanosPerTick = minecraftServer.getTickManager().getNanosPerTick();
|
||||||
|
sleeperExecutor.schedule(() -> minecraftServer.send(this), nanosPerTick * 16, TimeUnit.NANOSECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
log.info("{}", minecraftServer.getTicks());
|
||||||
|
int nextExecIn = (lastExecTick + TICK_DELAY) - minecraftServer.getTicks();
|
||||||
|
long nanosPerTick = minecraftServer.getTickManager().getNanosPerTick();
|
||||||
|
if (nextExecIn > 0) {
|
||||||
|
minecraftServer.send(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.lastExecTick = minecraftServer.getTicks();
|
||||||
|
minecraftServer.getProfiler().push("teleportLoaded");
|
||||||
|
teleportLoaded();
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
log.info("there are currently {} entities awaiting teleportation", toTeleport.size());
|
||||||
|
if (!toTeleport.isEmpty()) {
|
||||||
|
minecraftServer.send(this);
|
||||||
|
} else {
|
||||||
|
tickScheduled.set(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void teleportLoaded() {
|
||||||
|
Iterator<TeleportRequest> iterator = toTeleport.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
TeleportRequest teleportRequest = iterator.next();
|
||||||
|
if (teleportRequest == null) {
|
||||||
|
//empty for some reason!
|
||||||
|
iterator.remove();
|
||||||
|
log.error("null teleportRequest");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ServerWorld fromWorld = minecraftServer.getWorld(RegistryKey.of(RegistryKeys.WORLD, teleportRequest.fromWorldId));
|
||||||
|
if (fromWorld == null) {
|
||||||
|
iterator.remove();
|
||||||
|
log.error("lost from world");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ServerWorld toWorld = minecraftServer.getWorld(RegistryKey.of(RegistryKeys.WORLD, teleportRequest.toWorldId));
|
||||||
|
if (toWorld == null) {
|
||||||
|
iterator.remove();
|
||||||
|
log.error("lost to world");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Entity entity = fromWorld.getEntity(teleportRequest.entity);
|
||||||
|
if (entity == null) {
|
||||||
|
iterator.remove();
|
||||||
|
log.error("lost entity");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int x = teleportRequest.to.getX();
|
||||||
|
int z = teleportRequest.to.getZ();
|
||||||
|
if (toWorld.getChunkManager().isChunkLoaded(ChunkSectionPos.getSectionCoord(x), ChunkSectionPos.getSectionCoord(z))) {
|
||||||
|
log.info("chunkLoaded {}", teleportRequest.to);
|
||||||
|
minecraftServer.getProfiler().push("tryTeleport");
|
||||||
|
tryTeleport(entity, teleportRequest.from, fromWorld, teleportRequest.to, toWorld);
|
||||||
|
minecraftServer.getProfiler().pop();
|
||||||
|
iterator.remove();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
toWorld.getChunkManager()
|
||||||
|
.addTicket(ChunkTicketType.PORTAL, new ChunkPos(teleportRequest.to), 2, teleportRequest.to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void markForTeleport(Entity entity, Vector3f from, ServerWorld serverWorld) {
|
||||||
|
((ServerWorldAccessor) serverWorld).getEntityList()
|
||||||
|
.remove(entity);
|
||||||
|
boolean originalInvulnerability = entity.isInvulnerable();
|
||||||
|
entity.setInvulnerable(true);
|
||||||
|
getInstance(serverWorld.getServer())
|
||||||
|
.addTeleportRequest(entity, from, serverWorld, originalInvulnerability);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TeleportRequest(UUID entity, Vector3f from,
|
||||||
|
Identifier fromWorldId,
|
||||||
|
boolean originalInvulnerability,
|
||||||
|
BlockPos to,
|
||||||
|
Identifier toWorldId) implements Comparable<TeleportRequest> {
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull TeleportHelper.TeleportRequest other) {
|
||||||
|
Comparator<TeleportRequest> comparator = Comparator
|
||||||
|
.comparing((TeleportRequest teleportRequest) -> teleportRequest.entity);
|
||||||
|
return comparator.compare(this, other);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package quimufu.colourful_portals.util;
|
||||||
|
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.EntityDimensions;
|
||||||
|
import net.minecraft.particle.ParticleTypes;
|
||||||
|
import net.minecraft.util.math.Box;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
import org.joml.Random;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
|
||||||
|
public class TeleportParticleHelper {
|
||||||
|
private static final Random random = new Random();
|
||||||
|
|
||||||
|
public static void spawnTeleportationParticles(Entity entity, World world) {
|
||||||
|
for (int i = 0; i < 64; ++i) {
|
||||||
|
Box box = entity.getBoundingBox();
|
||||||
|
float xOffset = (float) ((AdditionalMath.root(random.nextFloat() * 2 - 1, 3) / 2) * box.getLengthX() * 2);
|
||||||
|
float yOffset = (float) (AdditionalMath.root(random.nextFloat(), 3) * box.getLengthY() * 2);
|
||||||
|
float zOffset = (float) ((AdditionalMath.root(random.nextFloat() * 2 - 1, 3) / 2) * box.getLengthZ() * 2);
|
||||||
|
|
||||||
|
Vector3f offset = new Vector3f(xOffset, yOffset, zOffset);
|
||||||
|
EntityDimensions dimensions = EntityDimensions.fixed(0.25f, 0.25f);
|
||||||
|
Vector3f endPos = randomInside(dimensions.getBoxAt(entity.getPos())).add(offset);
|
||||||
|
world.addParticle(ParticleTypes.PORTAL,
|
||||||
|
endPos.x, endPos.y, endPos.z,
|
||||||
|
-offset.x, -offset.y, -offset.z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Vector3f randomInside(Box box) {
|
||||||
|
return box.getMinPos().toVector3f()
|
||||||
|
.add(((float) box.getLengthX()) * random.nextFloat(),
|
||||||
|
((float) box.getLengthY()) * random.nextFloat(),
|
||||||
|
((float) box.getLengthZ()) * random.nextFloat());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,39 @@
|
|||||||
{
|
{
|
||||||
"item.colourful_portals.portal_fluid_bucket": "Farbeimer",
|
|
||||||
"block.colourful_portals.portal_block": "Farbenfrohes Portal",
|
"block.colourful_portals.portal_block": "Farbenfrohes Portal",
|
||||||
|
"block.colourful_portals.portal_fluid_block": "Farbenfrohe Flüssigkeit",
|
||||||
|
"colourful_portals.midnightconfig.disableImmersivePortals": "Immersive Portals integration deaktivieren",
|
||||||
|
"colourful_portals.midnightconfig.black": "Blöcke, die schwarze Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.blue": "Blöcke, die blaue Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.brown": "Blöcke, die braune Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.cyan": "Blöcke, die cyanfarbene Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.explanation1": "Diese Konfiguration erlaubt es, zusätzliche/andere Portalblocke zu definieren.\nPortale aus diesen verbinden sich mit anderen aus dem selben Block.",
|
||||||
|
"colourful_portals.midnightconfig.explanation2": "Die Liste, der der Block hinzugefügt wird, bestimmt dabei nur die Portalfarbe.\nÄnderungen treten für neue Portale oder bei Aktualisierung eines Portals in Kraft.",
|
||||||
|
"colourful_portals.midnightconfig.gray": "Blöcke, die graue Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.green": "Blöcke, die grüne Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.light_blue": "Blöcke, die hellblaue Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.light_gray": "Blöcke, die hellgraue Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.lime": "Blöcke, die limonenfarbene Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.magenta": "Blöcke, die magentafarbene Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.none": "Blöcke, die vollständig transparente Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.orange": "Blöcke, die orangefarbene Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.pink": "Blöcke, die rosa Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.purple": "Blöcke, die violette Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.red": "Blöcke, die rote Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.white": "Blöcke, die weiße Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.yellow": "Blöcke, die gelbe Portale erzeugen",
|
||||||
|
"colourful_portals.midnightconfig.blockyPortalFluid": "Das alte Aussehen von der farbenfrohen Flüssigkeit wiederherstellen",
|
||||||
|
"colourful_portals.midnightconfig.pearlDimensionWeights": "Die Gewichtungen für verschiedene Dimensionen bei Dimensionswechsel (bearbeitbar nur über die Konfigurationsdatei)",
|
||||||
|
"colourful_portals.midnightconfig.pearlSameDimensionLikelihood": "Die Wahrscheinlichkeit bei einer Teleportation mit einer Farbenfrohen Perle in der selben Dimension zu verbleiben",
|
||||||
|
"colourful_portals.midnightconfig.maxPearlDistance": "Der Mindestabstand, um den der Spieler von einer farbenfrohen Perle teleportiert werden",
|
||||||
|
"colourful_portals.midnightconfig.minPearlDistance": "Der Maximalabstand, um den der Spieler von einer farbenfrohen Perle teleportiert werden",
|
||||||
|
"colourful_portals.midnightconfig.category.portal_colours": "Portalblöcke",
|
||||||
|
"colourful_portals.midnightconfig.category.visuals": "Visuell",
|
||||||
|
"colourful_portals.midnightconfig.category.colourful_pearl": "Farbenfrohe Perle",
|
||||||
|
"colourful_portals.midnightconfig.category.integrations": "Mod Integrationen",
|
||||||
|
"subtitles.colourful_portals.entity.colourful_pearl.teleport_away": "Farbenfrohe Perle teleportiert weg",
|
||||||
"item.colourful_portals.colour_blob_bright": "Heller Farbmix",
|
"item.colourful_portals.colour_blob_bright": "Heller Farbmix",
|
||||||
"item.colourful_portals.colour_blob_dark": "Dunkler Farbmix",
|
"item.colourful_portals.colour_blob_dark": "Dunkler Farbmix",
|
||||||
"block.colourful_portals.portal_fluid_block": "Farbenfrohe Flüssigkeit"
|
"item.colourful_portals.portal_fluid_bucket": "Farbeimer",
|
||||||
|
"tag.block.c.slime_balls": "Schleimbälle",
|
||||||
|
"tag.block.c.crystals": "Kristalle"
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,39 @@
|
|||||||
{
|
{
|
||||||
"item.colourful_portals.portal_fluid_bucket": "Colourful Bucket",
|
"block.colourful_portals.portal_block": "Colourful Portal",
|
||||||
|
"block.colourful_portals.portal_fluid_block": "Colourful Fluid",
|
||||||
|
"colourful_portals.midnightconfig.disableImmersivePortals": "Disable Immersive Portals integration",
|
||||||
|
"colourful_portals.midnightconfig.black": "Blocks that create black portals",
|
||||||
|
"colourful_portals.midnightconfig.blue": "Blocks that create blue portals",
|
||||||
|
"colourful_portals.midnightconfig.brown": "Blocks that create brown portals",
|
||||||
|
"colourful_portals.midnightconfig.cyan": "Blocks that create cyan portals",
|
||||||
|
"colourful_portals.midnightconfig.explanation1": "This configuration allows you to define additional/other portal blocks.\nPortals from these connect to others from the same block.",
|
||||||
|
"colourful_portals.midnightconfig.explanation2": "The list to which the block is added only determines the portal color.\nChanges take effect for new portals or when a portal is updated.",
|
||||||
|
"colourful_portals.midnightconfig.gray": "Blocks that create gray portals",
|
||||||
|
"colourful_portals.midnightconfig.green": "Blocks that create green portals",
|
||||||
|
"colourful_portals.midnightconfig.light_blue": "Blocks that create light blue portals",
|
||||||
|
"colourful_portals.midnightconfig.light_gray": "Blocks that create light gray portals",
|
||||||
|
"colourful_portals.midnightconfig.lime": "Blocks that create lime portals",
|
||||||
|
"colourful_portals.midnightconfig.magenta": "Blocks that create magenta portals",
|
||||||
|
"colourful_portals.midnightconfig.none": "Blocks that create fully transparent portals",
|
||||||
|
"colourful_portals.midnightconfig.orange": "Blocks that create orange portals",
|
||||||
|
"colourful_portals.midnightconfig.pink": "Blocks that create pink portals",
|
||||||
|
"colourful_portals.midnightconfig.purple": "Blocks that create purple portals",
|
||||||
|
"colourful_portals.midnightconfig.red": "Blocks that create red portals",
|
||||||
|
"colourful_portals.midnightconfig.white": "Blocks that create white portals",
|
||||||
|
"colourful_portals.midnightconfig.yellow": "Blocks that create yellow portals",
|
||||||
|
"colourful_portals.midnightconfig.blockyPortalFluid": "Use the old Portal Fluid look",
|
||||||
|
"colourful_portals.midnightconfig.pearlDimensionWeights": "The weight for the different dimensions on dimension switch (only editable in config file)",
|
||||||
|
"colourful_portals.midnightconfig.pearlSameDimensionLikelihood": "Likelihood for a colourful pearl to teleport within the same dimension",
|
||||||
|
"colourful_portals.midnightconfig.maxPearlDistance": "The maximal distance a colourful pearl will teleport the player",
|
||||||
|
"colourful_portals.midnightconfig.minPearlDistance": "The minimal distance a colourful pearl will teleport the player",
|
||||||
|
"colourful_portals.midnightconfig.category.portal_colours": "Portal Blocks",
|
||||||
|
"colourful_portals.midnightconfig.category.visuals": "Visual",
|
||||||
|
"colourful_portals.midnightconfig.category.colourful_pearl": "Colourful Pearl",
|
||||||
|
"colourful_portals.midnightconfig.category.integrations": "Mod Integrations",
|
||||||
|
"subtitles.colourful_portals.entity.colourful_pearl.teleport_away": "Colourful pearl teleporting away",
|
||||||
"item.colourful_portals.colour_blob_bright": "Bright Colour Mix",
|
"item.colourful_portals.colour_blob_bright": "Bright Colour Mix",
|
||||||
"item.colourful_portals.colour_blob_dark": "Dark Colour Mix",
|
"item.colourful_portals.colour_blob_dark": "Dark Colour Mix",
|
||||||
"block.colourful_portals.portal_block": "Colourful Portal",
|
"item.colourful_portals.portal_fluid_bucket": "Colourful Bucket",
|
||||||
"block.colourful_portals.portal_fluid_block": "Colourful Fluid"
|
"tag.block.c.slime_balls": "Slime Balls",
|
||||||
|
"tag.block.c.crystals": "Crystals"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "minecraft:item/honey_bottle"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{ "predicate": { "color_id": 0 }, "model": "colourful_portals:item/colourful_air_bottle/white" },
|
||||||
|
{ "predicate": { "color_id": 1 }, "model": "colourful_portals:item/colourful_air_bottle/orange" },
|
||||||
|
{ "predicate": { "color_id": 2 }, "model": "colourful_portals:item/colourful_air_bottle/magenta" },
|
||||||
|
{ "predicate": { "color_id": 3 }, "model": "colourful_portals:item/colourful_air_bottle/light_blue" },
|
||||||
|
{ "predicate": { "color_id": 4 }, "model": "colourful_portals:item/colourful_air_bottle/yellow" },
|
||||||
|
{ "predicate": { "color_id": 5 }, "model": "colourful_portals:item/colourful_air_bottle/lime" },
|
||||||
|
{ "predicate": { "color_id": 6 }, "model": "colourful_portals:item/colourful_air_bottle/pink" },
|
||||||
|
{ "predicate": { "color_id": 7 }, "model": "colourful_portals:item/colourful_air_bottle/gray" },
|
||||||
|
{ "predicate": { "color_id": 8 }, "model": "colourful_portals:item/colourful_air_bottle/light_gray" },
|
||||||
|
{ "predicate": { "color_id": 9 }, "model": "colourful_portals:item/colourful_air_bottle/cyan" },
|
||||||
|
{ "predicate": { "color_id": 10 }, "model": "colourful_portals:item/colourful_air_bottle/purple" },
|
||||||
|
{ "predicate": { "color_id": 11 }, "model": "colourful_portals:item/colourful_air_bottle/blue" },
|
||||||
|
{ "predicate": { "color_id": 12 }, "model": "colourful_portals:item/colourful_air_bottle/brown" },
|
||||||
|
{ "predicate": { "color_id": 13 }, "model": "colourful_portals:item/colourful_air_bottle/green" },
|
||||||
|
{ "predicate": { "color_id": 14 }, "model": "colourful_portals:item/colourful_air_bottle/red" },
|
||||||
|
{ "predicate": { "color_id": 15 }, "model": "colourful_portals:item/colourful_air_bottle/black" }
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/black"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/blue"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/brown"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/cyan"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/gray"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/green"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/light_blue"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/light_gray"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/lime"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/magenta"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/orange"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/pink"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/purple"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/red"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/white"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_air_bottle/yellow"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "colourful_portals:item/colourful_pearl"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/black_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/blue_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/brown_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"black_colourful_air_0_0",
|
||||||
|
"black_colourful_air_0_1",
|
||||||
|
"black_colourful_air_1_0",
|
||||||
|
"black_colourful_air_1_1",
|
||||||
|
"black_colourful_air_2_0",
|
||||||
|
"black_colourful_air_2_1",
|
||||||
|
"black_colourful_air_3_0",
|
||||||
|
"black_colourful_air_3_1",
|
||||||
|
"blue_colourful_air_0_0",
|
||||||
|
"blue_colourful_air_0_1",
|
||||||
|
"blue_colourful_air_1_0",
|
||||||
|
"blue_colourful_air_1_1",
|
||||||
|
"blue_colourful_air_2_0",
|
||||||
|
"blue_colourful_air_2_1",
|
||||||
|
"blue_colourful_air_3_0",
|
||||||
|
"blue_colourful_air_3_1",
|
||||||
|
"brown_colourful_air_0_0",
|
||||||
|
"brown_colourful_air_0_1",
|
||||||
|
"brown_colourful_air_1_0",
|
||||||
|
"brown_colourful_air_1_1",
|
||||||
|
"brown_colourful_air_2_0",
|
||||||
|
"brown_colourful_air_2_1",
|
||||||
|
"brown_colourful_air_3_0",
|
||||||
|
"brown_colourful_air_3_1",
|
||||||
|
"cyan_colourful_air_0_0",
|
||||||
|
"cyan_colourful_air_0_1",
|
||||||
|
"cyan_colourful_air_1_0",
|
||||||
|
"cyan_colourful_air_1_1",
|
||||||
|
"cyan_colourful_air_2_0",
|
||||||
|
"cyan_colourful_air_2_1",
|
||||||
|
"cyan_colourful_air_3_0",
|
||||||
|
"cyan_colourful_air_3_1",
|
||||||
|
"green_colourful_air_0_0",
|
||||||
|
"green_colourful_air_0_1",
|
||||||
|
"green_colourful_air_1_0",
|
||||||
|
"green_colourful_air_1_1",
|
||||||
|
"green_colourful_air_2_0",
|
||||||
|
"green_colourful_air_2_1",
|
||||||
|
"green_colourful_air_3_0",
|
||||||
|
"green_colourful_air_3_1",
|
||||||
|
"grey_colourful_air_0_0",
|
||||||
|
"grey_colourful_air_0_1",
|
||||||
|
"grey_colourful_air_1_0",
|
||||||
|
"grey_colourful_air_1_1",
|
||||||
|
"grey_colourful_air_2_0",
|
||||||
|
"grey_colourful_air_2_1",
|
||||||
|
"grey_colourful_air_3_0",
|
||||||
|
"grey_colourful_air_3_1",
|
||||||
|
"light_blue_colourful_air_0_0",
|
||||||
|
"light_blue_colourful_air_0_1",
|
||||||
|
"light_blue_colourful_air_1_0",
|
||||||
|
"light_blue_colourful_air_1_1",
|
||||||
|
"light_blue_colourful_air_2_0",
|
||||||
|
"light_blue_colourful_air_2_1",
|
||||||
|
"light_blue_colourful_air_3_0",
|
||||||
|
"light_blue_colourful_air_3_1",
|
||||||
|
"light_grey_colourful_air_0_0",
|
||||||
|
"light_grey_colourful_air_0_1",
|
||||||
|
"light_grey_colourful_air_1_0",
|
||||||
|
"light_grey_colourful_air_1_1",
|
||||||
|
"light_grey_colourful_air_2_0",
|
||||||
|
"light_grey_colourful_air_2_1",
|
||||||
|
"light_grey_colourful_air_3_0",
|
||||||
|
"light_grey_colourful_air_3_1",
|
||||||
|
"lime_colourful_air_0_0",
|
||||||
|
"lime_colourful_air_0_1",
|
||||||
|
"lime_colourful_air_1_0",
|
||||||
|
"lime_colourful_air_1_1",
|
||||||
|
"lime_colourful_air_2_0",
|
||||||
|
"lime_colourful_air_2_1",
|
||||||
|
"lime_colourful_air_3_0",
|
||||||
|
"lime_colourful_air_3_1",
|
||||||
|
"magenta_colourful_air_0_0",
|
||||||
|
"magenta_colourful_air_0_1",
|
||||||
|
"magenta_colourful_air_1_0",
|
||||||
|
"magenta_colourful_air_1_1",
|
||||||
|
"magenta_colourful_air_2_0",
|
||||||
|
"magenta_colourful_air_2_1",
|
||||||
|
"magenta_colourful_air_3_0",
|
||||||
|
"magenta_colourful_air_3_1",
|
||||||
|
"orange_colourful_air_0_0",
|
||||||
|
"orange_colourful_air_0_1",
|
||||||
|
"orange_colourful_air_1_0",
|
||||||
|
"orange_colourful_air_1_1",
|
||||||
|
"orange_colourful_air_2_0",
|
||||||
|
"orange_colourful_air_2_1",
|
||||||
|
"orange_colourful_air_3_0",
|
||||||
|
"orange_colourful_air_3_1",
|
||||||
|
"pink_colourful_air_0_0",
|
||||||
|
"pink_colourful_air_0_1",
|
||||||
|
"pink_colourful_air_1_0",
|
||||||
|
"pink_colourful_air_1_1",
|
||||||
|
"pink_colourful_air_2_0",
|
||||||
|
"pink_colourful_air_2_1",
|
||||||
|
"pink_colourful_air_3_0",
|
||||||
|
"pink_colourful_air_3_1",
|
||||||
|
"purple_colourful_air_0_0",
|
||||||
|
"purple_colourful_air_0_1",
|
||||||
|
"purple_colourful_air_1_0",
|
||||||
|
"purple_colourful_air_1_1",
|
||||||
|
"purple_colourful_air_2_0",
|
||||||
|
"purple_colourful_air_2_1",
|
||||||
|
"purple_colourful_air_3_0",
|
||||||
|
"purple_colourful_air_3_1",
|
||||||
|
"red_colourful_air_0_0",
|
||||||
|
"red_colourful_air_0_1",
|
||||||
|
"red_colourful_air_1_0",
|
||||||
|
"red_colourful_air_1_1",
|
||||||
|
"red_colourful_air_2_0",
|
||||||
|
"red_colourful_air_2_1",
|
||||||
|
"red_colourful_air_3_0",
|
||||||
|
"red_colourful_air_3_1",
|
||||||
|
"white_colourful_air_0_0",
|
||||||
|
"white_colourful_air_0_1",
|
||||||
|
"white_colourful_air_1_0",
|
||||||
|
"white_colourful_air_1_1",
|
||||||
|
"white_colourful_air_2_0",
|
||||||
|
"white_colourful_air_2_1",
|
||||||
|
"white_colourful_air_3_0",
|
||||||
|
"white_colourful_air_3_1",
|
||||||
|
"yellow_colourful_air_0_0",
|
||||||
|
"yellow_colourful_air_0_1",
|
||||||
|
"yellow_colourful_air_1_0",
|
||||||
|
"yellow_colourful_air_1_1",
|
||||||
|
"yellow_colourful_air_2_0",
|
||||||
|
"yellow_colourful_air_2_1",
|
||||||
|
"yellow_colourful_air_3_0",
|
||||||
|
"yellow_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/cyan_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/grey_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/green_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_blue_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/light_grey_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/lime_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/magenta_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/orange_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/pink_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/purple_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/red_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/white_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"textures": [
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_0_0",
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_0_1",
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_1_0",
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_1_1",
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_2_0",
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_2_1",
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_3_0",
|
||||||
|
"colourful_portals:colourful_air_particle/yellow_colourful_air_3_1"
|
||||||
|
]
|
||||||
|
}
|
||||||
11
src/main/resources/assets/colourful_portals/sounds.json
Normal file
11
src/main/resources/assets/colourful_portals/sounds.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"entity.colourful_pearl.teleport_away": {
|
||||||
|
"subtitle": "subtitles.colourful_portals.entity.colourful_pearl.teleport_away",
|
||||||
|
"sounds": [
|
||||||
|
{
|
||||||
|
"name": "colourful_portals:teleport_away",
|
||||||
|
"attenuation_distance": 64
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user