Compare commits
12 Commits
265d71ae67
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a9ce715750 | |||
| 7aefd13755 | |||
| 562a16c4f8 | |||
| b4582640b7 | |||
| 01ba9ca366 | |||
| 4ee1a066ac | |||
| 9545a46171 | |||
| a654eec852 | |||
| 0d1e607206 | |||
| 4aa2643a76 | |||
| b1cd170c60 | |||
| ec041f9269 |
65
build.gradle
65
build.gradle
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id "com.modrinth.minotaur" version "2.+"
|
||||
id 'fabric-loom' version '1.2-SNAPSHOT'
|
||||
id 'fabric-loom' version '1.7-SNAPSHOT'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@ repositories {
|
||||
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
|
||||
// for more information about repositories.
|
||||
maven {
|
||||
name = 'Ladysnake Mods'
|
||||
url = 'https://ladysnake.jfrog.io/artifactory/mods'
|
||||
name = "Ladysnake Mods"
|
||||
url = 'https://maven.ladysnake.org/releases'
|
||||
}
|
||||
maven {
|
||||
url = "https://api.modrinth.com/maven"
|
||||
@@ -50,48 +50,27 @@ dependencies {
|
||||
|
||||
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
|
||||
|
||||
// Dependency of Immersive Portals Core:
|
||||
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)
|
||||
}
|
||||
modImplementation ("com.github.iPortalTeam:ImmersivePortalsMod:${project.immersive_portals_version}")
|
||||
|
||||
// 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
|
||||
include(modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-level:${project.cardinal_components_version}")) {
|
||||
exclude(group: "net.fabricmc.fabric-loader")
|
||||
transitive(false)
|
||||
}
|
||||
modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}"
|
||||
// Includes Cardinal Components API as a Jar-in-Jar dependency (optional but recommended)
|
||||
include "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}"
|
||||
|
||||
api("com.github.LlamaLad7:MixinExtras:0.2.0-beta.4")
|
||||
annotationProcessor("com.github.LlamaLad7:MixinExtras:0.2.0-beta.4")
|
||||
// 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}"
|
||||
|
||||
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"))
|
||||
//Config lib
|
||||
modImplementation "maven.modrinth:midnightlib:${project.midnightlib_version}"
|
||||
include "maven.modrinth:midnightlib:${project.midnightlib_version}"
|
||||
|
||||
modImplementation("maven.modrinth:sodium:${project.sodium_version}")
|
||||
}
|
||||
|
||||
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}"
|
||||
loom {
|
||||
accessWidenerPath = file("src/main/resources/colourful_portals.accesswidener")
|
||||
}
|
||||
|
||||
configurations.include.transitive = true
|
||||
@@ -109,7 +88,7 @@ processResources {
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
it.options.release = 17
|
||||
it.options.release = 21
|
||||
}
|
||||
|
||||
java {
|
||||
@@ -118,8 +97,8 @@ java {
|
||||
// If you remove this line, sources will not be generated.
|
||||
withSourcesJar()
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
jar {
|
||||
@@ -136,7 +115,7 @@ modrinth {
|
||||
versionType = "beta"
|
||||
dependencies = [
|
||||
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
|
||||
|
||||
@@ -4,21 +4,20 @@ org.gradle.parallel=true
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://fabricmc.net/develop
|
||||
minecraft_version=1.20.1
|
||||
yarn_mappings=1.20.1+build.8
|
||||
loader_version=0.14.21
|
||||
minecraft_version_major=1.20
|
||||
minecraft_version=1.21.1
|
||||
minecraft_version_major=1.21
|
||||
yarn_mappings=1.21.1+build.3
|
||||
loader_version=0.16.7
|
||||
|
||||
# Mod Properties
|
||||
mod_version=0.9.3
|
||||
mod_version=0.9.7
|
||||
maven_group=quimufu.colourful-portals
|
||||
archives_base_name=colourful-portals
|
||||
|
||||
# Dependencies
|
||||
fabric_version=0.84.0+1.20.1
|
||||
cardinal_components_version=5.2.1
|
||||
immersive_portals_version_short=3.1.0
|
||||
immersive_portals_version=v3.1.0-mc1.20.1
|
||||
tweed_version=1.3.0+mc1.20-pre1
|
||||
sodium_version=0.4.10
|
||||
cloth_config_version=11.0.99
|
||||
fabric_version=0.105.0+1.21.1
|
||||
cca_version = 6.1.1
|
||||
immersive_portals_version_short=6.0.3
|
||||
immersive_portals_version=v6.0.3-mc1.21.1
|
||||
sodium_version=mc1.21-0.5.11
|
||||
midnightlib_version=1.6.3-fabric
|
||||
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
|
||||
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
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
24
gradlew
vendored
24
gradlew
vendored
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (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.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -83,7 +83,8 @@ done
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
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.
|
||||
MAX_FD=maximum
|
||||
@@ -130,10 +131,13 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
@@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# 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" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
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.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
||||
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
||||
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;
|
||||
|
||||
import eu.midnightdust.lib.config.MidnightConfig;
|
||||
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.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.enums.Instrument;
|
||||
import net.minecraft.block.enums.NoteBlockInstrument;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.SpawnGroup;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.particle.SimpleParticleType;
|
||||
import net.minecraft.registry.Registries;
|
||||
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.SoundEvent;
|
||||
import net.minecraft.util.DyeColor;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.Rarity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -19,25 +32,62 @@ import net.minecraft.world.BlockView;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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.PortalFluidBlock;
|
||||
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 static final String MOD_ID = "colourful_portals";
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||
|
||||
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 BlockItem PORTAL_BLOCK_ITEM = new BlockItem(PORTAL_BLOCK, new FabricItemSettings().rarity(Rarity.EPIC));
|
||||
public static final Item BLOB_DARK = new Item(new FabricItemSettings());
|
||||
public static final Item BLOB_BRIGHT = new Item(new FabricItemSettings());
|
||||
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 ColourfulAirBottleItem COLOURFUL_AIR_BOTTLE_ITEM = new ColourfulAirBottleItem(COLOURFUL_AIR, new Item.Settings().rarity(Rarity.RARE).component(DataComponentTypes.BASE_COLOR, DyeColor.BLACK));
|
||||
|
||||
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 PortalFluidBlock PORTAL_FLUID_BLOCk = new PortalFluidBlock(PORTAL_FLUID, FabricBlockSettings.create().sounds(BlockSoundGroup.field_44608).luminance(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 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 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) {
|
||||
return false;
|
||||
@@ -50,23 +100,39 @@ public class ColourfulPortalsMod implements ModInitializer {
|
||||
|
||||
@Override
|
||||
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()) {
|
||||
PORTAL_BLOCKS.add(Identifier.tryParse(id));
|
||||
onConfigUpdate();
|
||||
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.ITEM, identifier, PORTAL_BLOCK_ITEM);
|
||||
|
||||
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "portal_fluid_bucket"), PORTAL_FLUID_BUCKET_ITEM);
|
||||
Registry.register(Registries.FLUID, new Identifier(MOD_ID, "portal_fluid"), PORTAL_FLUID);
|
||||
Registry.register(Registries.BLOCK, new Identifier(MOD_ID, "portal_fluid_block"), PORTAL_FLUID_BLOCk);
|
||||
Registry.register(Registries.BLOCK, Identifier.of(MOD_ID, "colourful_air"), COLOURFUL_AIR);
|
||||
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "colourful_air_bottle"), COLOURFUL_AIR_BOTTLE_ITEM);
|
||||
|
||||
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)
|
||||
.register(ColourfulPortalsMod::addToIngredients);
|
||||
@@ -74,9 +140,65 @@ public class ColourfulPortalsMod implements ModInitializer {
|
||||
ItemGroupEvents.modifyEntriesEvent(ItemGroups.TOOLS)
|
||||
.register(ColourfulPortalsMod::addTtTools);
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(this::onServerStarted);
|
||||
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) {
|
||||
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;
|
||||
|
||||
import dev.onyxstudios.cca.api.v3.component.ComponentKey;
|
||||
import dev.onyxstudios.cca.api.v3.component.ComponentRegistry;
|
||||
import dev.onyxstudios.cca.api.v3.level.LevelComponentFactoryRegistry;
|
||||
import dev.onyxstudios.cca.api.v3.level.LevelComponentInitializer;
|
||||
import org.ladysnake.cca.api.v3.component.ComponentKey;
|
||||
import org.ladysnake.cca.api.v3.component.ComponentRegistry;
|
||||
import org.ladysnake.cca.api.v3.level.LevelComponentFactoryRegistry;
|
||||
import org.ladysnake.cca.api.v3.level.LevelComponentInitializer;
|
||||
import net.minecraft.util.Identifier;
|
||||
import quimufu.colourful_portals.portal.PortalListComponent;
|
||||
|
||||
public class Components implements LevelComponentInitializer {
|
||||
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 =
|
||||
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
|
||||
public void registerLevelComponentFactories(LevelComponentFactoryRegistry registry) {
|
||||
registry.register(PORTAL_LIST, PortalListComponent::new);
|
||||
registry.register(PORTAL_CANDIDATE_LIST, PortalListComponent::new);
|
||||
registry.register(PORTAL_LIST, worldProperties1 -> new PortalListComponent());
|
||||
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 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
|
||||
|
||||
@@ -4,6 +4,7 @@ import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FluidFillable;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
@@ -14,14 +15,15 @@ import net.minecraft.state.property.EnumProperty;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.BlockRotation;
|
||||
import net.minecraft.util.DyeColor;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.*;
|
||||
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 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 {
|
||||
@@ -61,20 +63,19 @@ public class PortalBlock extends Block implements FluidFillable {
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos blockPos, ShapeContext shapeContext) {
|
||||
if (shapeContext.isHolding(Items.DEBUG_STICK)
|
||||
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_BLOCK_ITEM)
|
||||
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_FLUID_BUCKET_ITEM)) {
|
||||
return switch (state.get(AXIS)) {
|
||||
case Z -> Z_AABB;
|
||||
case Y -> Y_AABB;
|
||||
default -> X_AABB;
|
||||
};
|
||||
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_BLOCK_ITEM)
|
||||
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_FLUID_BUCKET_ITEM)) {
|
||||
return getShape(state);
|
||||
}
|
||||
return VoxelShapes.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return VoxelShapes.empty();
|
||||
public VoxelShape getShape(BlockState state) {
|
||||
return switch (state.get(AXIS)) {
|
||||
case Z -> Z_AABB;
|
||||
case Y -> Y_AABB;
|
||||
default -> X_AABB;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -100,7 +101,6 @@ public class PortalBlock extends Block implements FluidFillable {
|
||||
|
||||
public BlockState getStateWith(DyeColor color, Direction.Axis axis) {
|
||||
return this.getDefaultState().with(AXIS, axis).with(DYE_COLOR, color);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,22 +119,23 @@ public class PortalBlock extends Block implements FluidFillable {
|
||||
}
|
||||
|
||||
@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) {
|
||||
return true;
|
||||
}
|
||||
return fluid == ColourfulPortalsMod.PORTAL_FLUID
|
||||
&& (world instanceof ServerWorld)
|
||||
&& PortalManager.canExtend((ServerWorld) world, pos);
|
||||
&& (world instanceof ServerWorld)
|
||||
&& PORTAL_MANAGER.canExtend((ServerWorld) world, pos);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean tryFillWithFluid(WorldAccess world, BlockPos pos, BlockState state, FluidState fluidState) {
|
||||
if (world.isClient() && fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)) {
|
||||
return true;
|
||||
}
|
||||
return fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)
|
||||
&& (world instanceof ServerWorld)
|
||||
&& PortalManager.extend((ServerWorld) world, pos);
|
||||
&& (world instanceof ServerWorld)
|
||||
&& 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.FluidRenderHandlerRegistry;
|
||||
@@ -8,15 +8,25 @@ 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.PortalFluid;
|
||||
|
||||
public class CommonPortalFluidRenderer {
|
||||
public class CommonPortalFluidRendererV1 implements CommonPortalFluidRenderer {
|
||||
|
||||
public static final Quaternionf IDENTITY = new Quaternionf();
|
||||
private Sprite sprite = null;
|
||||
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());
|
||||
sprite = handler.getFluidSprites(null, null, fluidState)[0];
|
||||
@@ -60,11 +70,11 @@ public class CommonPortalFluidRenderer {
|
||||
FluidState neighbourFluidState = neighbour.getFluidState();
|
||||
if (axis == null || direction.getAxis() != axis) {
|
||||
if (neighbour.isSideSolidFullSquare(world, neighbourPos, direction.getOpposite())
|
||||
&& neighbour.isOpaque()) {
|
||||
&& neighbour.isOpaque()) {
|
||||
continue;
|
||||
}
|
||||
if (neighbourFluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)
|
||||
&& neighbourFluidState.get(PortalFluid.AMOUNT) >= amount) {
|
||||
&& neighbourFluidState.get(PortalFluid.AMOUNT) >= amount) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -72,7 +82,7 @@ public class CommonPortalFluidRenderer {
|
||||
|
||||
int neighbourAmount;
|
||||
if (neighbourFluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)
|
||||
&& (neighbourAmount = neighbourFluidState.get(PortalFluid.AMOUNT)) < amount) {
|
||||
&& (neighbourAmount = neighbourFluidState.get(PortalFluid.AMOUNT)) < amount) {
|
||||
//draw side as two little silvers, adapting our width to neighbour width
|
||||
float neighbourOffsetInSquishedDir = (16 - neighbourAmount) / 32F;
|
||||
drawSilvers(vertexEater, direction, axis, offsetInSquishedDir, neighbourOffsetInSquishedDir, offset, (amount & 1) == 1);
|
||||
@@ -81,10 +91,10 @@ public class CommonPortalFluidRenderer {
|
||||
}
|
||||
calcSquishedUVs(axis, 1.f - offsetInSquishedDir, offsetInSquishedDir, direction, (amount & 1) == 1);
|
||||
} else {
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(16.);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(16.);
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.F);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(1.F);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.F);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(1.F);
|
||||
|
||||
}
|
||||
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) {
|
||||
case X -> {
|
||||
if (odd) {
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.5 + squishedBottom * 16D);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(0.5 + squishedTop * 16D);
|
||||
uIu2IvIv2[0] = sprite.getFrameU(1.F / 32.F + squishedBottom);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(1.F / 32.F + squishedTop);
|
||||
|
||||
} else {
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0. + squishedBottom * 16D);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(squishedTop * 16D);
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.F + squishedBottom);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(squishedTop);
|
||||
}
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(16.);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.F);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(1.F);
|
||||
}
|
||||
case Z -> {
|
||||
if (direction.getAxis() == Direction.Axis.Y) {
|
||||
if (odd) {
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.5 + squishedBottom * 16D);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(0.5 + squishedTop * 16D);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(1.F / 32.F + squishedBottom);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(1.F / 32.F + squishedTop);
|
||||
|
||||
} else {
|
||||
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom * 16D);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(squishedTop * 16D);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(squishedTop);
|
||||
}
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(16.);
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.F);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(1.F);
|
||||
} else {
|
||||
if (odd) {
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.5 + squishedBottom * 16D);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(0.5 + squishedTop * 16D);
|
||||
uIu2IvIv2[0] = sprite.getFrameU(1.F / 32.F + squishedBottom);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(1.F / 32.F + squishedTop);
|
||||
|
||||
} else {
|
||||
uIu2IvIv2[0] = sprite.getFrameU(squishedBottom * 16D);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(squishedTop * 16D);
|
||||
uIu2IvIv2[0] = sprite.getFrameU(squishedBottom);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(squishedTop);
|
||||
}
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(16.);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.F);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(1.F);
|
||||
|
||||
}
|
||||
}
|
||||
case Y -> {
|
||||
if (odd) {
|
||||
uIu2IvIv2[2] = sprite.getFrameV(0.5 + squishedBottom * 16D);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(0.5 + squishedTop * 16D);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(1.F / 32.F + squishedBottom);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(1.F / 32.F + squishedTop);
|
||||
|
||||
} else {
|
||||
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom * 16D);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(squishedTop * 16D);
|
||||
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom);
|
||||
uIu2IvIv2[3] = sprite.getFrameV(squishedTop);
|
||||
}
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(16.);
|
||||
uIu2IvIv2[0] = sprite.getFrameU(0.F);
|
||||
uIu2IvIv2[1] = sprite.getFrameU(1.F);
|
||||
}
|
||||
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 x2 = 1F;
|
||||
@@ -190,62 +200,56 @@ public class CommonPortalFluidRenderer {
|
||||
z2 = 1F - offsetInSquishedDir;
|
||||
}
|
||||
}
|
||||
calcSquishedUVs(axis, 1.f - neighbourOffsetInSquishedDir,1.f - offsetInSquishedDir , direction, odd);
|
||||
calcSquishedUVs(axis, 1.f - neighbourOffsetInSquishedDir, 1.f - offsetInSquishedDir, direction, odd);
|
||||
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);
|
||||
switch (direction) {
|
||||
case DOWN -> {
|
||||
vertexConsumer.eatVertex(x, y, z, frameU, frameV2);
|
||||
vertexConsumer.eatVertex( x2, y, z, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex( x2, y, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex( x, y, z2, frameU, frameV);
|
||||
vertexConsumer.drawQuad(offset, direction);
|
||||
vertexConsumer.eatVertex(x2, y, z, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex(x, y, z2, frameU, frameV);
|
||||
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||
}
|
||||
case UP -> {
|
||||
vertexConsumer.eatVertex( x, y2, z2, frameU, frameV2);
|
||||
vertexConsumer.eatVertex( x2, y2, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex( x2, y2, z, frameU2, frameV);
|
||||
vertexConsumer.eatVertex( x, y2, z, frameU, frameV);
|
||||
vertexConsumer.drawQuad(offset, direction);
|
||||
vertexConsumer.eatVertex(x, y2, z2, frameU, frameV2);
|
||||
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex(x2, y2, z, frameU2, frameV);
|
||||
vertexConsumer.eatVertex(x, y2, z, frameU, frameV);
|
||||
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||
}
|
||||
case NORTH -> {
|
||||
vertexConsumer.eatVertex( x, y2, z, frameU, frameV);
|
||||
vertexConsumer.eatVertex( x2, y2, z, frameU2, frameV);
|
||||
vertexConsumer.eatVertex( x2, y, z, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex( x, y, z, frameU, frameV2);
|
||||
vertexConsumer.drawQuad(offset, direction);
|
||||
vertexConsumer.eatVertex(x, y2, z, frameU, frameV);
|
||||
vertexConsumer.eatVertex(x2, y2, z, frameU2, frameV);
|
||||
vertexConsumer.eatVertex(x2, y, z, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex(x, y, z, frameU, frameV2);
|
||||
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||
}
|
||||
case SOUTH -> {
|
||||
vertexConsumer.eatVertex( x, y, z2, frameU, frameV);
|
||||
vertexConsumer.eatVertex( x2, y, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex( x2, y2, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex( x, y2, z2, frameU, frameV2);
|
||||
vertexConsumer.drawQuad(offset, direction);
|
||||
vertexConsumer.eatVertex(x, y, z2, frameU, frameV);
|
||||
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex(x, y2, z2, frameU, frameV2);
|
||||
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||
}
|
||||
case WEST -> {
|
||||
vertexConsumer.eatVertex( x, y, z, frameU, frameV);
|
||||
vertexConsumer.eatVertex( x, y, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex( x, y2, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex( x, y2, z, frameU, frameV2);
|
||||
vertexConsumer.drawQuad(offset, direction);
|
||||
vertexConsumer.eatVertex(x, y, z, frameU, frameV);
|
||||
vertexConsumer.eatVertex(x, y, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex(x, y2, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex(x, y2, z, frameU, frameV2);
|
||||
vertexConsumer.drawQuad(offset, IDENTITY, direction);
|
||||
}
|
||||
case EAST -> {
|
||||
vertexConsumer.eatVertex( x2, y2, z, frameU, frameV);
|
||||
vertexConsumer.eatVertex( x2, y2, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex( x2, y, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex( x2, y, z, frameU, frameV2);
|
||||
vertexConsumer.drawQuad(offset, direction);
|
||||
vertexConsumer.eatVertex(x2, y2, z, frameU, frameV);
|
||||
vertexConsumer.eatVertex(x2, y2, z2, frameU2, frameV);
|
||||
vertexConsumer.eatVertex(x2, y, z2, frameU2, frameV2);
|
||||
vertexConsumer.eatVertex(x2, y, z, frameU, frameV2);
|
||||
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.Environment;
|
||||
@@ -13,18 +13,21 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
import quimufu.colourful_portals.ColourfulPortalsMod;
|
||||
import quimufu.colourful_portals.client.ColourfulPortalsModClient;
|
||||
|
||||
@Environment(value = EnvType.CLIENT)
|
||||
public class PortalFluidRenderHandler implements FluidRenderHandler, CommonPortalFluidRenderer.VertexEater {
|
||||
Identifier PORTAL_FLUID_STILL = new Identifier(ColourfulPortalsMod.MOD_ID, "block/portal_still");
|
||||
public class PortalFluidRenderHandler implements FluidRenderHandler, VertexEater {
|
||||
Identifier PORTAL_FLUID_STILL = Identifier.of(ColourfulPortalsMod.MOD_ID, "block/portal_still");
|
||||
|
||||
private final CommonPortalFluidRenderer commonPortalFluidRenderer = new CommonPortalFluidRenderer();
|
||||
private Sprite sprite = null;
|
||||
private final ThreadLocal<VertexConsumer> vertexConsumer = new ThreadLocal<>();
|
||||
private final ThreadLocal<Integer> i = new ThreadLocal<>();
|
||||
|
||||
private final ThreadLocal<float[][]> vertices = new ThreadLocal<>();
|
||||
private final ThreadLocal<Vector3f> tmpVector = ThreadLocal.withInitial(Vector3f::new);
|
||||
|
||||
@Override
|
||||
public Sprite[] getFluidSprites(@Nullable BlockRenderView view, @Nullable BlockPos pos, FluidState state) {
|
||||
@@ -39,11 +42,13 @@ public class PortalFluidRenderHandler implements FluidRenderHandler, CommonPorta
|
||||
@Override
|
||||
public void renderFluid(BlockPos pos, BlockRenderView world, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) {
|
||||
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) {
|
||||
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
|
||||
public void drawQuad(BlockPos offset, Direction direction) {
|
||||
float offX = offset.getX();
|
||||
float offY = offset.getY();
|
||||
float offZ = offset.getZ();
|
||||
public void drawQuad(Vector3f offset, Quaternionf rotation, Direction direction) {
|
||||
float offX = offset.x;
|
||||
float offY = offset.y;
|
||||
float offZ = offset.z;
|
||||
Vector3f f = tmpVector.get();
|
||||
for (float[] v : vertices.get()) {
|
||||
|
||||
f.set(v);
|
||||
rotation.transform(f);
|
||||
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)
|
||||
.texture(v[3], v[4])
|
||||
.light(16)
|
||||
.normal(0.0f, 1.0f, 0.0f)
|
||||
.next();
|
||||
.normal(0.0f, 1.0f, 0.0f);
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import de.siphalor.tweed4.annotated.AConfigEntry;
|
||||
import de.siphalor.tweed4.annotated.ATweedConfig;
|
||||
import de.siphalor.tweed4.config.ConfigEnvironment;
|
||||
import de.siphalor.tweed4.config.ConfigScope;
|
||||
//import de.siphalor.tweed4.tailor.cloth.ClothData;
|
||||
import com.google.common.collect.Lists;
|
||||
import eu.midnightdust.lib.config.MidnightConfig;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
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)
|
||||
//@ClothData(modid = ColourfulPortalsMod.MOD_ID)
|
||||
public class ColourfulPortalConfig {
|
||||
public class ColourfulPortalConfig extends MidnightConfig {
|
||||
|
||||
@AConfigEntry(comment =
|
||||
"""
|
||||
Allows you to add additional blocks as portalBlocks,
|
||||
assigning them the color of the portal plane.
|
||||
null causes the portals out of that block to have *no* plane.
|
||||
Changes in the plane color will only apply to new portals,
|
||||
or on portal update.
|
||||
""")
|
||||
public static Map<String, DyeColor> portalBlocks = ImmutableMap.ofEntries(
|
||||
Map.entry("minecraft:white_wool", DyeColor.WHITE),
|
||||
Map.entry("minecraft:orange_wool", DyeColor.ORANGE),
|
||||
Map.entry("minecraft:magenta_wool", DyeColor.MAGENTA),
|
||||
Map.entry("minecraft:light_blue_wool", DyeColor.LIGHT_BLUE),
|
||||
Map.entry("minecraft:yellow_wool", DyeColor.YELLOW),
|
||||
Map.entry("minecraft:lime_wool", DyeColor.LIME),
|
||||
Map.entry("minecraft:pink_wool", DyeColor.PINK),
|
||||
Map.entry("minecraft:gray_wool", DyeColor.GRAY),
|
||||
Map.entry("minecraft:light_gray_wool", DyeColor.LIGHT_GRAY),
|
||||
Map.entry("minecraft:cyan_wool", DyeColor.CYAN),
|
||||
Map.entry("minecraft:purple_wool", DyeColor.PURPLE),
|
||||
Map.entry("minecraft:blue_wool", DyeColor.BLUE),
|
||||
Map.entry("minecraft:brown_wool", DyeColor.BROWN),
|
||||
Map.entry("minecraft:green_wool", DyeColor.GREEN),
|
||||
Map.entry("minecraft:red_wool", DyeColor.RED),
|
||||
Map.entry("minecraft:black_wool", DyeColor.BLACK));
|
||||
@Entry(category = "integrations", name = "Disable Immersive Portals integration")
|
||||
public static boolean disableImmersivePortals = false;
|
||||
|
||||
@Comment(category = "portal_colours")
|
||||
public static Comment explanation1;
|
||||
@Comment(category = "portal_colours")
|
||||
public static Comment explanation2;
|
||||
|
||||
@Entry(category = "portal_colours")
|
||||
public static List<String> white = Lists.newArrayList("minecraft:white_wool");
|
||||
|
||||
@Entry(category = "portal_colours")
|
||||
public static List<String> orange = Lists.newArrayList("minecraft:orange_wool");
|
||||
|
||||
@Entry(category = "portal_colours")
|
||||
public static List<String> magenta = Lists.newArrayList("minecraft:magenta_wool");
|
||||
|
||||
@Entry(category = "portal_colours")
|
||||
public static List<String> light_blue = Lists.newArrayList("minecraft:light_blue_wool");
|
||||
|
||||
@Entry(category = "portal_colours")
|
||||
public static List<String> yellow = Lists.newArrayList("minecraft:yellow_wool");
|
||||
|
||||
@Entry(category = "portal_colours")
|
||||
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.BlockState;
|
||||
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.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.Vec3i;
|
||||
import net.minecraft.util.math.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -24,21 +26,21 @@ public class PortalHelper {
|
||||
{'N', 'X', 'X', 'N'},
|
||||
};
|
||||
|
||||
static Iterator<BlockPos> insideOf(BlockBox portal) {
|
||||
Direction axisW = getAxisW(portal);
|
||||
static Iterator<BlockPos> insideOf(PortalRepresentation portal) {
|
||||
BlockBox portal1 = portal.location();
|
||||
Direction axisW = getAxisW(portal1);
|
||||
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');
|
||||
|
||||
}
|
||||
|
||||
static boolean isValidPortal(ServerWorld world, BlockBox portal, Identifier blockId) {
|
||||
return isValidPortal(world, portal, blockId, true, false);
|
||||
static boolean isValidPortal(ServerWorld world, PortalRepresentation portal, Identifier blockId) {
|
||||
return isValidPortal(world, portal.location(), blockId, true, false);
|
||||
}
|
||||
|
||||
static boolean isValidCandidate(ServerWorld world, BlockBox portal, Identifier blockId) {
|
||||
return isValidPortal(world, portal, blockId, false, false);
|
||||
static boolean isValidCandidate(ServerWorld world, PortalRepresentation portal, Identifier blockId) {
|
||||
return isValidPortal(world, portal.location(), blockId, false, false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static Direction getAxisW(PortalRepresentation portalRepresentation) {
|
||||
return getAxisW(portalRepresentation.location());
|
||||
}
|
||||
|
||||
static Direction getAxisW(BlockBox fromPortalBox) {
|
||||
if (fromPortalBox.getMaxX() - fromPortalBox.getMinX() > 2)
|
||||
return Direction.EAST;
|
||||
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);
|
||||
ArrayList<BlockBox> portals = new ArrayList<>();
|
||||
ArrayList<PortalRepresentation> portals = new ArrayList<>();
|
||||
outer:
|
||||
for (Direction direction : Direction.values()) {
|
||||
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 upwardsOffset = Direction.UP.getVector().multiply(4);
|
||||
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();
|
||||
}
|
||||
|
||||
public static boolean isPortalPlaceable(ServerWorld world, BlockBox portal, Identifier blockId) {
|
||||
return isValidPortal(world, portal, blockId, false, true);
|
||||
public static boolean isPortalPlaceable(ServerWorld world, PortalRepresentation portal, Identifier blockId) {
|
||||
return isValidPortal(world, portal.location(), blockId, false, true);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@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 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;
|
||||
|
||||
import dev.onyxstudios.cca.api.v3.component.Component;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.nbt.NbtList;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockBox;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.WorldProperties;
|
||||
import quimufu.colourful_portals.util.Pair;
|
||||
import org.ladysnake.cca.api.v3.component.Component;
|
||||
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.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
||||
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
|
||||
|
||||
public class PortalListComponent implements Component {
|
||||
private final WorldProperties worldProperties;
|
||||
HashMap<Identifier, List<Pair<BlockBox, Identifier>>> portalsPerPortalBlock = new HashMap<>();
|
||||
public static final int CURRENT_VERSION = 1;
|
||||
HashMap<Identifier, LinkedList<PortalRepresentation>> portalsPerPortalBlock = new HashMap<>();
|
||||
|
||||
public PortalListComponent(WorldProperties worldProperties) {
|
||||
this.worldProperties = worldProperties;
|
||||
Identifier lastPortalLinkingSystem = Identifier.of(MOD_ID, "immersive_portals_linking_system");
|
||||
|
||||
public PortalListComponent() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFromNbt(NbtCompound tag) {
|
||||
public void readFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
|
||||
if (tag != null && !tag.isEmpty()) {
|
||||
for (String block : tag.getKeys()) {
|
||||
NbtList portalsWithDim = tag.getList(block, NbtElement.COMPOUND_TYPE);
|
||||
ArrayList<Pair<BlockBox, Identifier>> portals = new ArrayList<>();
|
||||
if (!tag.contains("version")) {
|
||||
readFromOldNbt(tag);
|
||||
}
|
||||
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++) {
|
||||
NbtCompound portalWithDim = portalsWithDim.getCompound(i);
|
||||
|
||||
NbtCompound portalCompound = portalWithDim.getCompound("portal");
|
||||
BlockBox portal = BlockBox.create(NbtHelper.toBlockPos(portalCompound.getCompound("from")),
|
||||
NbtHelper.toBlockPos(portalCompound.getCompound("to")));
|
||||
BlockBox portal = BlockBox.create(NbtHelper.toBlockPos(portalCompound, "from").orElseThrow(),
|
||||
NbtHelper.toBlockPos(portalCompound, "to").orElseThrow());
|
||||
|
||||
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
|
||||
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()) {
|
||||
NbtList portalsWithDimList = new NbtList();
|
||||
tag.put(portalBlockId.toString(), portalsWithDimList);
|
||||
for (Pair<BlockBox, Identifier> portalWithDim : portalsPerPortalBlock.get(portalBlockId)) {
|
||||
blocks.put(portalBlockId.toString(), portalsWithDimList);
|
||||
for (PortalRepresentation portalRepresentationWithDim : portalsPerPortalBlock.get(portalBlockId)) {
|
||||
NbtCompound portalWithDimCompound = new NbtCompound();
|
||||
portalsWithDimList.add(portalWithDimCompound);
|
||||
|
||||
NbtCompound portalCompound = new NbtCompound();
|
||||
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 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) {
|
||||
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>()).stream()
|
||||
.filter((portalWithDim) -> (portalWithDim.second.equals(dim)
|
||||
&& portalWithDim.first.contains(pos)))
|
||||
.map((p) -> p.first)
|
||||
public List<PortalRepresentation> getContainingPortals(Identifier blockId, BlockPos pos, Identifier dim) {
|
||||
return getPortals(blockId).stream()
|
||||
.filter((portal) -> (portal.dimensionId().equals(dim) && portal.location().contains(pos)))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public void createPortal(Identifier blockId, Pair<BlockBox, Identifier> portalWithDim) {
|
||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
||||
if (!portals.contains(portalWithDim)) {
|
||||
portals.add(portalWithDim);
|
||||
public void createPortal(Identifier blockId, PortalRepresentation portal) {
|
||||
LinkedList<PortalRepresentation> portalRepresentations = getPortals(blockId);
|
||||
if (!portalRepresentations.contains(portal)) {
|
||||
portalRepresentations.add(portal);
|
||||
}
|
||||
}
|
||||
|
||||
public void removePortal(Identifier blockId, Pair<BlockBox, Identifier> portalWithDim) {
|
||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
||||
portals.remove(portalWithDim);
|
||||
public void removePortal(Identifier blockId, PortalRepresentation portal) {
|
||||
getPortals(blockId).remove(portal);
|
||||
}
|
||||
|
||||
public List<Pair<BlockBox, Identifier>> getPortals(Identifier blockId) {
|
||||
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
||||
public LinkedList<PortalRepresentation> getPortals(Identifier blockId) {
|
||||
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new LinkedList<>());
|
||||
}
|
||||
|
||||
|
||||
public Set<Identifier> getBlockIds() {
|
||||
return portalsPerPortalBlock.keySet();
|
||||
}
|
||||
|
||||
public Pair<BlockBox, Identifier> getNext(Identifier blockId, BlockBox portal, Identifier dim) {
|
||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
||||
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal, dim);
|
||||
|
||||
int nextIndex = portals.indexOf(portalWithDim) + 1;
|
||||
if (nextIndex >= portals.size()) {
|
||||
return portals.get(0);
|
||||
public PortalRepresentation getNext(Identifier blockId, PortalRepresentation portalRepresentationWithDim) {
|
||||
LinkedList<PortalRepresentation> portals = getPortals(blockId);
|
||||
Node<PortalRepresentation> node;
|
||||
if ((node = portals.getNodeOf(portalRepresentationWithDim)) == null) {
|
||||
return portals.getFirst();
|
||||
}
|
||||
|
||||
return portals.get(nextIndex);
|
||||
return node.getNext() == null ? portals.getFirst() : node.getNext().getValue();
|
||||
}
|
||||
|
||||
public boolean containsPortal(Identifier blockId, Pair<BlockBox,Identifier> portal) {
|
||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
||||
return portals.contains(portal);
|
||||
public boolean containsPortal(Identifier blockId, PortalRepresentation portalRepresentation) {
|
||||
return getPortals(blockId).contains(portalRepresentation);
|
||||
}
|
||||
|
||||
public Pair<BlockBox, Identifier> getLast(Identifier blockId) {
|
||||
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
|
||||
return portals.get(portals.size()-1);
|
||||
public PortalRepresentation getLast(Identifier blockId) {
|
||||
return getPortals(blockId).getLast();
|
||||
}
|
||||
|
||||
public Identifier lastPortalLinkingSystemId() {
|
||||
return lastPortalLinkingSystem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,106 +3,106 @@ package quimufu.colourful_portals.portal;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
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.DyeColor;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.TypeFilter;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.world.World;
|
||||
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 net.minecraft.util.math.BlockBox;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
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.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
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 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");
|
||||
LOGGER.debug("onPortalBlockPlaced, {}", blockId);
|
||||
|
||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
||||
Identifier dimIdentifier = PortalHelper.getDimId(world);
|
||||
|
||||
//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);
|
||||
for (BlockBox portalCandidate : portalCandidates) {
|
||||
for (PortalRepresentation portalCandidate : portalCandidates) {
|
||||
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
|
||||
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
|
||||
List<BlockBox> portals = PortalHelper.findPortalCandidates(world, pos, blockId);
|
||||
List<PortalRepresentation> portals = PortalHelper.findPortalCandidates(world, pos, blockId);
|
||||
LOGGER.debug("new portalCandidates found, {}", portals);
|
||||
|
||||
for (BlockBox portal : portals) {
|
||||
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal, world.getDimensionKey().getValue());
|
||||
portalCandidateList.createPortal(blockId, portalWithDim);
|
||||
for (PortalRepresentation portal : portals) {
|
||||
portalCandidateList.createPortal(blockId, portal);
|
||||
}
|
||||
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");
|
||||
LOGGER.debug("onPortalBlockBroken, {}", blockId);
|
||||
|
||||
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
|
||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
||||
|
||||
//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);
|
||||
for (BlockBox portalCandidate : portalCandidates) {
|
||||
for (PortalRepresentation portalCandidate : portalCandidates) {
|
||||
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
|
||||
LOGGER.debug("invalid, {}", portalCandidate);
|
||||
Pair<BlockBox, Identifier> portalWithDim =
|
||||
Pair.of(portalCandidate, world.getDimensionKey().getValue());
|
||||
portalCandidateList.removePortal(blockId, portalWithDim);
|
||||
portalCandidateList.removePortal(blockId, portalCandidate);
|
||||
}
|
||||
}
|
||||
|
||||
//add portalCandidates deobstructed by PortalBlock removal
|
||||
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);
|
||||
|
||||
PortalListComponent portalListComponent = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
||||
Pair<BlockBox, Identifier> portalWithDim =
|
||||
Pair.of(newPortal, world.getDimensionKey().getValue());
|
||||
portalListComponent.createPortal(blockId, portalWithDim);
|
||||
portalCandidateList.createPortal(blockId, newPortal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//check portal validity
|
||||
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, world.getDimensionKey().getValue());
|
||||
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dimensionIdentifier);
|
||||
boolean changed = false;
|
||||
for (BlockBox portal : portals) {
|
||||
for (PortalRepresentation portal : portals) {
|
||||
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 =
|
||||
Pair.of(portal, world.getDimensionKey().getValue());
|
||||
portalList.removePortal(blockId, portalWithDim);
|
||||
destroyPortalEntitiesInside(world, Box.from(portal));
|
||||
removePortalBlocks(world, portal);
|
||||
portalList.removePortal(blockId, portal);
|
||||
linkingSystem.unLinkPortal(portal);
|
||||
removePortalBlocks(world, portal.location());
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -114,55 +114,53 @@ public class PortalManager {
|
||||
world.getProfiler().pop();
|
||||
}
|
||||
|
||||
private static void removePortalBlocks(ServerWorld world, BlockBox portal) {
|
||||
private void removePortalBlocks(ServerWorld world, BlockBox portal) {
|
||||
BlockPos.stream(portal)
|
||||
.filter(blockPos -> world.getBlockState(blockPos).isOf(PORTAL_BLOCK))
|
||||
.forEach(blockPos -> world.setBlockState(blockPos, Blocks.AIR.getDefaultState()));
|
||||
}
|
||||
|
||||
private static void fixPortalGroup(Identifier blockId, PortalListComponent portalList, MinecraftServer server) {
|
||||
List<Pair<BlockBox, Identifier>> portalsWithDim = portalList.getPortals(blockId);
|
||||
private void fixPortalGroup(Identifier blockId, PortalListComponent portalList, MinecraftServer server) {
|
||||
LinkedList<PortalRepresentation> portalsWithDim = portalList.getPortals(blockId);
|
||||
if (portalsWithDim.size() == 1) {
|
||||
Pair<BlockBox, Identifier> portal = portalsWithDim.get(0);
|
||||
ServerWorld world = getPortalWorld(portal, server);
|
||||
PortalRepresentation portalRepresentation = portalsWithDim.getFirst();
|
||||
ServerWorld world = PortalHelper.getPortalWorld(server, portalRepresentation);
|
||||
if (world == null) {
|
||||
LOGGER.error("error fixing portalGroup, world {} was null", portal.second);
|
||||
LOGGER.error("error fixing portalGroup, dimensionId {} was null", portalRepresentation.dimensionId());
|
||||
return;
|
||||
}
|
||||
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal.first, portal.second);
|
||||
portalList.removePortal(blockId, portalWithDim);
|
||||
destroyPortalEntitiesInside(world, Box.from(portal.first));
|
||||
removePortalBlocks(world, portal.first);
|
||||
portalList.removePortal(blockId, portalRepresentation);
|
||||
linkingSystem.unLinkPortal(portalRepresentation);
|
||||
removePortalBlocks(world, portalRepresentation.location());
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < portalsWithDim.size(); i++) {
|
||||
Pair<BlockBox, Identifier> portal = portalsWithDim.get(i);
|
||||
Pair<BlockBox, Identifier> linkedToPortal = i + 1 < portalsWithDim.size() ? portalsWithDim.get(i + 1) : portalsWithDim.get(0);
|
||||
assureLinkedTo(portal, linkedToPortal, server);
|
||||
assurePortalBlocksPlaced(blockId, portal, server);
|
||||
for (PortalRepresentation portalRepresentation : portalsWithDim) {
|
||||
assurePortalBlocksPlaced(blockId, portalRepresentation, server);
|
||||
}
|
||||
linkingSystem.linkPortals(portalsWithDim);
|
||||
|
||||
}
|
||||
|
||||
private static void assurePortalBlocksPlaced(Identifier blockId, Pair<BlockBox, Identifier> portal, MinecraftServer server) {
|
||||
DyeColor color = ColourfulPortalConfig.portalBlocks.get(blockId.toString());
|
||||
private void assurePortalBlocksPlaced(Identifier blockId, PortalRepresentation portalRepresentation, MinecraftServer server) {
|
||||
DyeColor color = ColourfulPortalConfig.colorOf(blockId.toString());
|
||||
if (color != null) {
|
||||
Direction.Axis portalPlaneAxis = PortalHelper.getAxisW(portal.first)
|
||||
Direction.Axis portalPlaneAxis = PortalHelper.getAxisW(portalRepresentation)
|
||||
.rotateClockwise(Direction.Axis.Y)
|
||||
.getAxis();
|
||||
ServerWorld portalWorld = getPortalWorld(portal, server);
|
||||
ServerWorld portalWorld = PortalHelper.getPortalWorld(server, portalRepresentation);
|
||||
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;
|
||||
}
|
||||
BlockState portalBlockState = PORTAL_BLOCK.getStateWith(color, portalPlaneAxis);
|
||||
|
||||
PortalHelper.insideOf(portal.first)
|
||||
PortalHelper.insideOf(portalRepresentation)
|
||||
.forEachRemaining(blockPos -> {
|
||||
BlockState blockState = portalWorld.getBlockState(blockPos);
|
||||
if (blockState.isAir() || blockState.getFluidState().isOf(PORTAL_FLUID)) {
|
||||
portalWorld.setBlockState(blockPos, portalBlockState);
|
||||
} else if (blockState.isReplaceable()) {
|
||||
portalWorld.breakBlock(blockPos,true);
|
||||
portalWorld.breakBlock(blockPos, true);
|
||||
portalWorld.setBlockState(blockPos, portalBlockState);
|
||||
}
|
||||
});
|
||||
@@ -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);
|
||||
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) {
|
||||
public boolean tryIgnite(ServerWorld world, BlockPos pos) {
|
||||
world.getProfiler().push("portal ignition");
|
||||
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
|
||||
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
|
||||
LOGGER.info("portal ignition");
|
||||
Set<Identifier> blockIds = portalCandidateList.getBlockIds();
|
||||
boolean ret = false;
|
||||
for (Identifier blockId : blockIds) {
|
||||
Identifier dim = world.getDimensionKey().getValue();
|
||||
List<BlockBox> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dim);
|
||||
for (BlockBox portalCandidate : portalCandidates) {
|
||||
if (PortalHelper.isValidPortal(world, portalCandidate, blockId)) {
|
||||
Pair<BlockBox, Identifier> current = Pair.of(portalCandidate, dim);
|
||||
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, portalCandidate, dim);
|
||||
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
|
||||
Identifier dim = PortalHelper.getDimId(world);
|
||||
List<PortalRepresentation> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dim);
|
||||
for (PortalRepresentation current : portalCandidates) {
|
||||
if (PortalHelper.isValidPortal(world, current, blockId)) {
|
||||
PortalRepresentation next = portalCandidateList.getNext(blockId, current);
|
||||
ServerWorld nextWorld = PortalHelper.getPortalWorld(world.getServer(), next);
|
||||
if (!next.equals(current)
|
||||
&& (!portalList.containsPortal(blockId, current) || !portalList.containsPortal(blockId, next))
|
||||
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)) {
|
||||
&& (!portalList.containsPortal(blockId, current) || !portalList.containsPortal(blockId, next))
|
||||
&& PortalHelper.isPortalPlaceable(nextWorld, next, blockId)) {
|
||||
portalList.createPortal(blockId, current);
|
||||
portalList.createPortal(blockId, next);
|
||||
ret = true;
|
||||
@@ -275,24 +196,21 @@ public class PortalManager {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static boolean canExtend(ServerWorld world, BlockPos pos) {
|
||||
public boolean canExtend(ServerWorld world, BlockPos pos) {
|
||||
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();
|
||||
Identifier dim = world.getDimensionKey().getValue();
|
||||
Identifier dim = PortalHelper.getDimId(world);
|
||||
|
||||
for (Identifier blockId : blockIds) {
|
||||
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, dim);
|
||||
for (BlockBox portal : portals) {
|
||||
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dim);
|
||||
for (PortalRepresentation portal : portals) {
|
||||
if (PortalHelper.isValidPortal(world, portal, blockId)) {
|
||||
Pair<BlockBox, Identifier> last = portalList.getLast(blockId);
|
||||
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, last.first, last.second);
|
||||
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
|
||||
PortalRepresentation last = portalList.getLast(blockId);
|
||||
PortalRepresentation next = portalCandidateList.getNext(blockId, last);
|
||||
ServerWorld nextWorld = PortalHelper.getPortalWorld(world.getServer(), next);
|
||||
|
||||
if(!portalList.containsPortal(blockId, next)
|
||||
&& PortalHelper.isValidPortal(world, portal, blockId)
|
||||
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)){
|
||||
if (!portalList.containsPortal(blockId, next)
|
||||
&& PortalHelper.isPortalPlaceable(nextWorld, next, blockId)) {
|
||||
world.getProfiler().pop();
|
||||
return true;
|
||||
}
|
||||
@@ -305,25 +223,22 @@ public class PortalManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean extend(ServerWorld world, BlockPos pos) {
|
||||
public boolean extend(ServerWorld world, BlockPos pos) {
|
||||
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();
|
||||
Identifier dim = world.getDimensionKey().getValue();
|
||||
Identifier dim = PortalHelper.getDimId(world);
|
||||
|
||||
boolean ret = false;
|
||||
for (Identifier blockId : blockIds) {
|
||||
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, dim);
|
||||
for (BlockBox portal : portals) {
|
||||
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dim);
|
||||
for (PortalRepresentation portal : portals) {
|
||||
if (PortalHelper.isValidPortal(world, portal, blockId)) {
|
||||
Pair<BlockBox, Identifier> last = portalList.getLast(blockId);
|
||||
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, last.first, last.second);
|
||||
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
|
||||
PortalRepresentation last = portalList.getLast(blockId);
|
||||
PortalRepresentation next = portalCandidateList.getNext(blockId, last);
|
||||
ServerWorld nextWorld = PortalHelper.getPortalWorld(world.getServer(), next);
|
||||
|
||||
if(!portalList.containsPortal(blockId, next)
|
||||
&& PortalHelper.isValidPortal(world, portal, blockId)
|
||||
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)){
|
||||
if (!portalList.containsPortal(blockId, next)
|
||||
&& PortalHelper.isPortalPlaceable(nextWorld, next, blockId)) {
|
||||
portalList.createPortal(blockId, next);
|
||||
ret = true;
|
||||
}
|
||||
@@ -336,4 +251,49 @@ public class PortalManager {
|
||||
world.getProfiler().pop();
|
||||
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;
|
||||
|
||||
NullableAxis(Direction.Axis axis) {
|
||||
|
||||
this.axis = axis;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import quimufu.colourful_portals.portal.PortalManager;
|
||||
import java.util.*;
|
||||
|
||||
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
|
||||
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_MANAGER;
|
||||
|
||||
public class PortalFluid extends Fluid {
|
||||
public static final EnumProperty<NullableAxis> AXIS = EnumProperty.of("axis", NullableAxis.class);
|
||||
@@ -138,7 +139,7 @@ public class PortalFluid extends Fluid {
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
//my state might have changed.
|
||||
@@ -169,7 +170,7 @@ public class PortalFluid extends Fluid {
|
||||
} else if (!world.isClient) {
|
||||
for (Direction direction : Direction.values()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -197,6 +198,8 @@ public class PortalFluid extends Fluid {
|
||||
}
|
||||
}
|
||||
Direction candidate = null;
|
||||
BlockState candidateBlockState = null;
|
||||
|
||||
for (Direction dir : Direction.values()) {
|
||||
if (dir == targetDir.getOpposite() || dir == targetDir) {
|
||||
continue;
|
||||
@@ -211,12 +214,16 @@ public class PortalFluid extends Fluid {
|
||||
//if adjacent to any solid Block, it's a candidate
|
||||
if (neighbourOfInspected.isSideSolidFullSquare(world, neighborOfInspectedLocation, dir.getOpposite())) {
|
||||
candidate = dir;
|
||||
candidateBlockState = world.getBlockState(pos.offset(dir));
|
||||
|
||||
}
|
||||
if (candidate == null && !neighbourOfInspected.isReplaceable()) {
|
||||
if ((candidate == null || candidateBlockState.isReplaceable()) && !neighbourOfInspected.isReplaceable()) {
|
||||
candidate = dir;
|
||||
candidateBlockState = world.getBlockState(pos.offset(dir));
|
||||
}
|
||||
if (candidate == null && !neighbourOfInspected.isReplaceable()) {
|
||||
if (candidate == null) {
|
||||
candidate = dir;
|
||||
candidateBlockState = world.getBlockState(pos.offset(dir));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -372,7 +379,7 @@ public class PortalFluid extends Fluid {
|
||||
BlockPos neighborOfInspectedLocation = inspectedLocation.offset(dir);
|
||||
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()))) {
|
||||
dirCost -= 2;
|
||||
discount = false;
|
||||
@@ -381,7 +388,7 @@ public class PortalFluid extends Fluid {
|
||||
//if adjacent to any solid Block, 1 less
|
||||
if (neighbourOfInspected.isSideSolidFullSquare(world, neighborOfInspectedLocation, dir.getOpposite())) {
|
||||
discount = true;
|
||||
//maybe a Portal Block in another direction
|
||||
//maybe a PortalRepresentation Block in another direction
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package quimufu.colourful_portals.portal_fluid;
|
||||
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.entity.ai.pathing.NavigationType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
@@ -18,6 +19,7 @@ import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -106,7 +108,7 @@ public class PortalFluidBlock
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
|
||||
protected boolean canPathfindThrough(BlockState state, NavigationType type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -116,7 +118,7 @@ public class PortalFluidBlock
|
||||
}
|
||||
|
||||
@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) {
|
||||
world.setBlockState(pos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL | Block.REDRAW_ON_MAIN_THREAD);
|
||||
return new ItemStack(this.fluid.getBucketItem());
|
||||
@@ -126,6 +128,7 @@ public class PortalFluidBlock
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<SoundEvent> getBucketFillSound() {
|
||||
return fluid.getBucketFillSound();
|
||||
|
||||
@@ -35,7 +35,7 @@ public class PortalFluidBucketItem extends BucketItem {
|
||||
BlockPos pos = blockHitResult.getBlockPos();
|
||||
BlockState blockState = world.getBlockState(pos);
|
||||
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)) {
|
||||
return TypedActionResult.success(BucketItem.getEmptiedStack(itemStack, user), world.isClient());
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class PortalFluidBucketItem extends BucketItem {
|
||||
Block block = blockState.getBlock();
|
||||
boolean shouldTryPlace = blockState.isAir()
|
||||
|| 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) {
|
||||
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_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_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_dark": "Dark Colour Mix",
|
||||
"block.colourful_portals.portal_block": "Colourful Portal",
|
||||
"block.colourful_portals.portal_fluid_block": "Colourful Fluid"
|
||||
"item.colourful_portals.portal_fluid_bucket": "Colourful Bucket",
|
||||
"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