Compare commits

...

5 Commits

Author SHA1 Message Date
a9ce715750 fix sodium compat 2024-10-11 20:57:23 +02:00
7aefd13755 finish update and fix immersive portals integration 2024-10-11 20:17:22 +02:00
562a16c4f8 WIP: colourful pearls 2024-10-11 16:06:40 +02:00
b4582640b7 add colourful pearl and air 2024-08-13 08:20:13 +02:00
01ba9ca366 add alternative portalRendering system 2024-07-29 21:25:00 +02:00
223 changed files with 2352 additions and 363 deletions

View File

@ -1,3 +1,3 @@
# Colourful Portals Reimagined
A Fabric Minecraft mod adding colourful portalRepresentations, based on Immersive Portals
A Fabric Minecraft mod adding colourful portals, based on Immersive Portals

View File

@ -1,6 +1,6 @@
plugins {
id "com.modrinth.minotaur" version "2.+"
id 'fabric-loom' version '1.6-SNAPSHOT'
id 'fabric-loom' version '1.7-SNAPSHOT'
id 'maven-publish'
}
@ -17,10 +17,6 @@ repositories {
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// 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'
}
maven {
name = "Ladysnake Mods"
url = 'https://maven.ladysnake.org/releases'
@ -54,7 +50,7 @@ dependencies {
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
modCompileOnly ("com.github.iPortalTeam:ImmersivePortalsMod:${project.immersive_portals_version}")
modImplementation ("com.github.iPortalTeam:ImmersivePortalsMod:${project.immersive_portals_version}")
// Replace modImplementation with modApi if you expose components in your own API
modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}"
@ -66,21 +62,11 @@ dependencies {
// 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("me.shedaniel.cloth:cloth-config-fabric:$project.cloth_config_version")
modCompileOnly("maven.modrinth:sodium:${project.sodium_version}")
modImplementation("maven.modrinth:sodium:${project.sodium_version}")
}
loom {

View File

@ -4,22 +4,20 @@ org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.21
minecraft_version=1.21.1
minecraft_version_major=1.21
yarn_mappings=1.21+build.2
loader_version=0.15.11
yarn_mappings=1.21.1+build.3
loader_version=0.16.7
# Mod Properties
mod_version=0.9.5.2
maven_group=quimufu.colourful-portalRepresentations
archives_base_name=colourful-portalRepresentations
mod_version=0.9.7
maven_group=quimufu.colourful-portals
archives_base_name=colourful-portals
# Dependencies
fabric_version=0.100.1+1.21
cca_version = 6.1.0
immersive_portals_version_short=5.1.7
immersive_portals_version=v5.1.7-mc1.20.4
tweed_version=1.3.0+mc1.20.2
sodium_version=mc1.21-0.5.9
cloth_config_version=15.0.127
midnightlib_version=1.5.7-fabric
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

Binary file not shown.

View File

@ -1,6 +1,7 @@
#Sun Jun 16 00:12:45 CEST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-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
View File

@ -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
View File

@ -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

View 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;
}
}

View File

@ -6,16 +6,25 @@ 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.particle.v1.FabricParticleTypes;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
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;
@ -23,13 +32,16 @@ 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.Comparator;
import java.util.HashSet;
import java.util.*;
import static quimufu.colourful_portals.Components.PORTAL_CANDIDATE_LIST;
import static quimufu.colourful_portals.Components.PORTAL_LIST;
@ -38,17 +50,42 @@ 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(AbstractBlock.Settings.create().instrument(NoteBlockInstrument.HAT).sounds(BlockSoundGroup.GLASS).strength(-1.0f, 3600000.8f).dropsNothing().nonOpaque().luminance((bs) -> 15).allowsSpawning(ColourfulPortalsMod::never).solidBlock(ColourfulPortalsMod::never).suffocates(ColourfulPortalsMod::never).blockVision(ColourfulPortalsMod::never).ticksRandomly());
public static final ColourfulAirBlock COLOURFUL_AIR = new ColourfulAirBlock(AbstractBlock.Settings.create().instrument(NoteBlockInstrument.HAT).sounds(BlockSoundGroup.GLASS).strength(-1.0f, 3600000.8f).dropsNothing().noCollision().luminance((bs) -> 7).allowsSpawning(ColourfulPortalsMod::never).solidBlock(ColourfulPortalsMod::never).suffocates(ColourfulPortalsMod::never).blockVision(ColourfulPortalsMod::never).ticksRandomly());
public static final 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, 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 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();
@ -65,21 +102,31 @@ public class ColourfulPortalsMod implements ModInitializer {
public void onInitialize() {
LOGGER.info("Colouring Portals...");
MidnightConfig.init(MOD_ID, ColourfulPortalConfig.class);
ColourfulPortalConfig.registerListener(this::onConfigUpdate);
for (String id : ColourfulPortalConfig.getAllPortalBlocks()) {
LOGGER.info(id);
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));
}
if (!ColourfulPortalConfig.disableImmersivePortals && hasImmPtl()) {
Registry.register(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY, ImmersivePortalsLinkingSystem.IMMERSIVE_PORTALS_LINKING_SYSTEM, new PrioritizedPortalLinkingSystemBuilder(ImmersivePortalsLinkingSystem::new, 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());
}
Registry.register(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY, DefaultLinkingSystem.DEFAULT_LINKING_SYSTEM, new PrioritizedPortalLinkingSystemBuilder(DefaultLinkingSystem::new, 90));
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.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.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);
@ -97,6 +144,18 @@ public class ColourfulPortalsMod implements ModInitializer {
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());
@ -112,7 +171,7 @@ public class ColourfulPortalsMod implements ModInitializer {
PortalListComponent portalList = PORTAL_LIST.get(minecraftServer.getSaveProperties().getMainWorldProperties());
PortalLinkingSystem currentLinkingSystem = getCurrentlyConfiguredLinkingSystem(minecraftServer);
if (currentLinkingSystem.getLinkingSystemId() != portalList.lastPortalLinkingSystemId() || currentLinkingSystem.needsReInit()) {
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);
@ -133,7 +192,8 @@ public class ColourfulPortalsMod implements ModInitializer {
private PortalLinkingSystem getCurrentlyConfiguredLinkingSystem(MinecraftServer minecraftServer) {
return PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY
.stream()
.min(Comparator.comparing(PrioritizedPortalLinkingSystemBuilder::priority))
.filter(prio -> prio.priority().getAsInt() >= 0)
.max(Comparator.comparing(prio -> prio.priority().getAsInt()))
.map(PrioritizedPortalLinkingSystemBuilder::portalLinkingSystemBuilder)
.orElse(DefaultLinkingSystem::new)
.build(minecraftServer);

View File

@ -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());
}
}

View File

@ -15,8 +15,8 @@ public class Components implements LevelComponentInitializer {
@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());
}
}

View File

@ -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.client.SodiumFluidRendererMixin", () -> FabricLoader.getInstance().isModLoaded("sodium")
"quimufu.colourful_portals.client.mixin.SodiumFluidRendererMixin", () -> FabricLoader.getInstance().isModLoaded("sodium")
);
@Override

View File

@ -4,7 +4,6 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.FluidFillable;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
@ -23,7 +22,6 @@ import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
import quimufu.colourful_portals.util.CollisionAwareShapeContext;
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_MANAGER;
@ -67,74 +65,18 @@ public class PortalBlock extends Block implements FluidFillable {
if (shapeContext.isHolding(Items.DEBUG_STICK)
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_BLOCK_ITEM)
|| shapeContext.isHolding(ColourfulPortalsMod.PORTAL_FLUID_BUCKET_ITEM)) {
return getShape(state);
}
return VoxelShapes.empty();
}
public VoxelShape getShape(BlockState state) {
return switch (state.get(AXIS)) {
case Z -> Z_AABB;
case Y -> Y_AABB;
default -> X_AABB;
};
}
return VoxelShapes.empty();
}
protected void onEntityCollisionOld(BlockState state, World world, BlockPos pos, Entity entity) {
ColourfulPortalsMod.LOGGER.info("detected collision {}", entity.getType());
if (entity.getBoundingBox()
.intersects(getShape(state, world, pos).getBoundingBox())) {
Vec3d vec3d = new Vec3d(0.5, 0.5f, 0.5);
entity.slowMovement(state, vec3d);
}
Vec3d entityPos = entity.getPos();
Vec3d prevEntityPos = new Vec3d(entity.prevX, entity.prevY, entity.prevZ);
if(entityPos.equals(prevEntityPos) && entity.getVelocity().squaredDistanceTo(Vec3d.ZERO) >= 0.01D){
//in this case, the collision logic was called prior to the movement logic,
// so we'll have to calculate our own next postion
entityPos = entityPos.add(entity.getVelocity());
}
Vec3d movement = entityPos.subtract(prevEntityPos);
if (world instanceof ServerWorld) {
Direction.Axis axis = state.get(AXIS);
double centerPosAlongAxis = pos.toCenterPos().getComponentAlongAxis(axis);
double entityPosAlongAxis = entityPos.getComponentAlongAxis(axis);
double prevEntityPosAlongAxis = prevEntityPos.getComponentAlongAxis(axis);
double movementAlongAxis = entityPosAlongAxis - prevEntityPosAlongAxis;
if (prevEntityPosAlongAxis > entityPosAlongAxis) {
if (entityPosAlongAxis < centerPosAlongAxis
&& prevEntityPosAlongAxis >= centerPosAlongAxis) {
double plainHitAfter = (centerPosAlongAxis - entityPosAlongAxis) / movementAlongAxis;
Vec3d planeHitPoint = entityPos.add(movement.multiply(plainHitAfter));
if (Box.from(new BlockBox(pos)).contains(planeHitPoint)) {
PORTAL_MANAGER.onPortalPassed(entity, pos, (ServerWorld) world, axis);
}
}
} else if (prevEntityPosAlongAxis < entityPosAlongAxis) {
if (entityPosAlongAxis > centerPosAlongAxis
&& prevEntityPosAlongAxis <= centerPosAlongAxis) {
double plainHitAfter = (centerPosAlongAxis - entityPosAlongAxis) / movementAlongAxis;
Vec3d planeHitPoint = entityPos.add(movement.multiply(plainHitAfter));
if (Box.from(new BlockBox(pos)).contains(planeHitPoint)) {
PORTAL_MANAGER.onPortalPassed(entity, pos, (ServerWorld) world, axis);
}
}
}
}
}
public VoxelShape getShape(BlockState state, BlockView world, BlockPos pos) {
return switch (state.get(AXIS)) {
case Z -> Z_AABB;
case Y -> Y_AABB;
default -> X_AABB;
};
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
if (context instanceof CollisionAwareShapeContext) {
return getShape(state, world, pos);
}
return VoxelShapes.empty();
}
@Override
public BlockState rotate(BlockState state, BlockRotation rotation) {
@ -159,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

View File

@ -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);
}
}
}

View File

@ -1,4 +1,4 @@
package quimufu.colourful_portals.mixin.client;
package quimufu.colourful_portals.client.mixin;
import net.minecraft.client.texture.Animator;
import net.minecraft.client.texture.SpriteContents;
@ -10,8 +10,8 @@ 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.AlphaBlendingAnimator;
import quimufu.colourful_portals.client.AlphaInterpolationHolder;
import quimufu.colourful_portals.client.rendering.AlphaBlendingAnimator;
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
import java.util.List;

View File

@ -1,9 +1,9 @@
package quimufu.colourful_portals.mixin.client;
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.AlphaInterpolationHolder;
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
@Mixin(AnimationResourceMetadata.class)
public class AnimationResourceMetadataMixin implements AlphaInterpolationHolder {

View File

@ -1,4 +1,4 @@
package quimufu.colourful_portals.mixin.client;
package quimufu.colourful_portals.client.mixin;
import com.google.gson.JsonObject;
import net.minecraft.client.resource.metadata.AnimationResourceMetadata;
@ -8,7 +8,7 @@ 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.AlphaInterpolationHolder;
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
@Mixin(AnimationResourceMetadataReader.class)
public class AnimationResourceMetadataReaderMixin {

View File

@ -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);
}

View File

@ -1,4 +1,4 @@
package quimufu.colourful_portals.mixin.client;
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;
@ -6,22 +6,24 @@ 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.SodiumPortalFluidRenderHandler;
import quimufu.colourful_portals.client.rendering.fluid.SodiumPortalFluidRenderHandler;
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_FLUID;
@Mixin(FluidRenderer.class)
public class SodiumFluidRendererMixin {
private final SodiumPortalFluidRenderHandler sodiumPortalFluidRenderHandler = new SodiumPortalFluidRenderHandler();
@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.render(world, fluidState, blockPos, offset, buffers);
sodiumPortalFluidRenderHandler.get().render(world, fluidState, blockPos, offset, buffers);
ci.cancel();
}
}

View File

@ -1,4 +1,4 @@
package quimufu.colourful_portals.mixin.client;
package quimufu.colourful_portals.client.mixin;
import net.minecraft.client.resource.metadata.AnimationResourceMetadata;
import net.minecraft.client.texture.SpriteContents;
@ -11,7 +11,7 @@ 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.AlphaInterpolationHolder;
import quimufu.colourful_portals.client.rendering.AlphaInterpolationHolder;
@Mixin(SpriteContents.class)
public abstract class SpriteContentsMixin {

View File

@ -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);
}
}

View File

@ -1,4 +1,4 @@
package quimufu.colourful_portals.client;
package quimufu.colourful_portals.client.rendering;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.texture.Animator;
@ -9,7 +9,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* This code is mostly copies from minecraft source. it is owned by mojang.
* This code is mostly copies from minecraft source. It is owned by mojang.
*/
public class AlphaBlendingAnimator implements Animator {
int frame;

View File

@ -1,4 +1,4 @@
package quimufu.colourful_portals.client;
package quimufu.colourful_portals.client.rendering;
public interface AlphaInterpolationHolder {

View File

@ -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);
}

View File

@ -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];
@ -98,8 +108,8 @@ public class CommonPortalFluidRenderer {
switch (axis) {
case X -> {
if (odd) {
uIu2IvIv2[0] = sprite.getFrameU(1.F/32.F + squishedBottom);
uIu2IvIv2[1] = sprite.getFrameU(1.F/32.F + squishedTop);
uIu2IvIv2[0] = sprite.getFrameU(1.F / 32.F + squishedBottom);
uIu2IvIv2[1] = sprite.getFrameU(1.F / 32.F + squishedTop);
} else {
uIu2IvIv2[0] = sprite.getFrameU(0.F + squishedBottom);
@ -111,8 +121,8 @@ public class CommonPortalFluidRenderer {
case Z -> {
if (direction.getAxis() == Direction.Axis.Y) {
if (odd) {
uIu2IvIv2[2] = sprite.getFrameV(1.F/32.F + squishedBottom);
uIu2IvIv2[3] = sprite.getFrameV(1.F/32.F + squishedTop);
uIu2IvIv2[2] = sprite.getFrameV(1.F / 32.F + squishedBottom);
uIu2IvIv2[3] = sprite.getFrameV(1.F / 32.F + squishedTop);
} else {
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom);
@ -122,8 +132,8 @@ public class CommonPortalFluidRenderer {
uIu2IvIv2[1] = sprite.getFrameU(1.F);
} else {
if (odd) {
uIu2IvIv2[0] = sprite.getFrameU(1.F/32.F + squishedBottom);
uIu2IvIv2[1] = sprite.getFrameU(1.F/32.F + squishedTop);
uIu2IvIv2[0] = sprite.getFrameU(1.F / 32.F + squishedBottom);
uIu2IvIv2[1] = sprite.getFrameU(1.F / 32.F + squishedTop);
} else {
uIu2IvIv2[0] = sprite.getFrameU(squishedBottom);
@ -136,8 +146,8 @@ public class CommonPortalFluidRenderer {
}
case Y -> {
if (odd) {
uIu2IvIv2[2] = sprite.getFrameV(1.F/32.F + squishedBottom);
uIu2IvIv2[3] = sprite.getFrameV(1.F/32.F + squishedTop);
uIu2IvIv2[2] = sprite.getFrameV(1.F / 32.F + squishedBottom);
uIu2IvIv2[3] = sprite.getFrameV(1.F / 32.F + squishedTop);
} else {
uIu2IvIv2[2] = sprite.getFrameV(squishedBottom);
@ -151,7 +161,7 @@ public class CommonPortalFluidRenderer {
}
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);
}
}

View File

@ -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;
}
}
}
}

View File

@ -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 {
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,14 +76,16 @@ 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)

View File

@ -1,17 +1,14 @@
package quimufu.colourful_portals.client;
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.model.quad.properties.ModelQuadWinding;
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.builder.ChunkMeshBufferBuilder;
import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder;
import me.jellysquid.mods.sodium.client.render.chunk.vertex.format.ChunkVertexEncoder.Vertex;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.texture.Sprite;
@ -19,18 +16,22 @@ 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 CommonPortalFluidRenderer.VertexEater {
public class SodiumPortalFluidRenderHandler implements VertexEater {
private Sprite sprite = null;
private final ModelQuadViewMutable quad = new ModelQuad();
private final CommonPortalFluidRenderer commonPortalFluidRenderer = new CommonPortalFluidRenderer();
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() {
}
@ -38,7 +39,9 @@ public class SodiumPortalFluidRenderHandler implements CommonPortalFluidRenderer
public boolean render(BlockRenderView world, FluidState fluidState, BlockPos pos, BlockPos offset, ChunkBuildBuffers buffers) {
material = DefaultMaterials.forFluidState(fluidState);
this.chunkModelBuilder = buffers.get(material);
return commonPortalFluidRenderer.render(world,fluidState,pos,offset,this);
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);
}
@ -54,23 +57,24 @@ public class SodiumPortalFluidRenderHandler implements CommonPortalFluidRenderer
quad.setZ(i, z);
quad.setTexU(i, u);
quad.setTexV(i, v);
i = (i+1)%4;
i = (i + 1) % 4;
}
@Override
public void drawQuad(BlockPos offset, Direction direction) {
writeQuad(chunkModelBuilder, material, offset, quad, ModelQuadFacing.fromDirection(direction), false);
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, BlockPos offset, ModelQuadView quad, ModelQuadFacing facing, boolean flip) {
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];
out.x = offset.getX() + quad.getX(i);
out.y = offset.getY() + quad.getY(i);
out.z = offset.getZ() + quad.getZ(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);
@ -88,5 +92,4 @@ public class SodiumPortalFluidRenderHandler implements CommonPortalFluidRenderer
}
}

View File

@ -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);
}

View File

@ -2,72 +2,99 @@ package quimufu.colourful_portals.config;
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 net.minecraft.world.World;
import quimufu.colourful_portals.ColourfulPortalsMod;
import quimufu.colourful_portals.util.Procedure;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
public class ColourfulPortalConfig extends MidnightConfig {
@Entry(category = "text", name = "Disable Immersive Portals integration")
@Entry(category = "integrations", name = "Disable Immersive Portals integration")
public static boolean disableImmersivePortals = false;
@Comment(category = "text")
public static Comment explanation;
@Comment(category = "portal_colours")
public static Comment explanation1;
@Comment(category = "portal_colours")
public static Comment explanation2;
@Entry(category = "text", name = "Blocks that create white portals")
@Entry(category = "portal_colours")
public static List<String> white = Lists.newArrayList("minecraft:white_wool");
@Entry(category = "text", name = "Blocks that create orange portals")
@Entry(category = "portal_colours")
public static List<String> orange = Lists.newArrayList("minecraft:orange_wool");
@Entry(category = "text", name = "Blocks that create magenta portals")
@Entry(category = "portal_colours")
public static List<String> magenta = Lists.newArrayList("minecraft:magenta_wool");
@Entry(category = "text", name = "Blocks that create light blue portals")
@Entry(category = "portal_colours")
public static List<String> light_blue = Lists.newArrayList("minecraft:light_blue_wool");
@Entry(category = "text", name = "Blocks that create yellow portals")
@Entry(category = "portal_colours")
public static List<String> yellow = Lists.newArrayList("minecraft:yellow_wool");
@Entry(category = "text", name = "Blocks that create lime portals")
@Entry(category = "portal_colours")
public static List<String> lime = Lists.newArrayList("minecraft:lime_wool");
@Entry(category = "text", name = "Blocks that create pink portals")
@Entry(category = "portal_colours")
public static List<String> pink = Lists.newArrayList("minecraft:pink_wool");
@Entry(category = "text", name = "Blocks that create gray portals")
@Entry(category = "portal_colours")
public static List<String> gray = Lists.newArrayList("minecraft:gray_wool");
@Entry(category = "text", name = "Blocks that create light gray portals")
@Entry(category = "portal_colours")
public static List<String> light_gray = Lists.newArrayList("minecraft:light_gray_wool");
@Entry(category = "text", name = "Blocks that create cyan portals")
@Entry(category = "portal_colours")
public static List<String> cyan = Lists.newArrayList("minecraft:cyan_wool");
@Entry(category = "text", name = "Blocks that create purple portals")
@Entry(category = "portal_colours")
public static List<String> purple = Lists.newArrayList("minecraft:purple_wool");
@Entry(category = "text", name = "Blocks that create blue portals")
@Entry(category = "portal_colours")
public static List<String> blue = Lists.newArrayList("minecraft:blue_wool");
@Entry(category = "text", name = "Blocks that create brown portals")
@Entry(category = "portal_colours")
public static List<String> brown = Lists.newArrayList("minecraft:brown_wool");
@Entry(category = "text", name = "Blocks that create green portals")
@Entry(category = "portal_colours")
public static List<String> green = Lists.newArrayList("minecraft:green_wool");
@Entry(category = "text", name = "Blocks that create red portals")
@Entry(category = "portal_colours")
public static List<String> red = Lists.newArrayList("minecraft:red_wool");
@Entry(category = "text", name = "Blocks that create black portals")
@Entry(category = "portal_colours")
public static List<String> black = Lists.newArrayList("minecraft:black_wool");
//todo: disabled until i find a good solution
//@Entry(category = "text", name = "Blocks that create fully transparent portals")
@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);
@ -129,4 +156,37 @@ public class ColourfulPortalConfig extends MidnightConfig {
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);
}
}
}

View File

@ -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();
}
}

View File

@ -26,7 +26,6 @@ public class LinkedList<E> implements List<E> {
size++;
node = node.getNext();
}
LOGGER.info("" + size);
return size;
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -74,16 +74,29 @@ public abstract class BlockChangeAndEntityMovementMixin {
// 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.info("onBlockNew {} -> {}", oldBlock, newBlock);
LOGGER.debug("onBlockNew {} -> {}", oldBlock, newBlock);
Identifier blockId = Registries.BLOCK.getId(newBlock.getBlock());
world.getServer().execute(() -> PORTAL_MANAGER.onPortalBlockPlaced(world, pos, blockId));
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.info("onBlockOld {} -> {}", oldBlock, newBlock);
LOGGER.debug("onBlockOld {} -> {}", oldBlock, newBlock);
Identifier blockId = Registries.BLOCK.getId(oldBlock.getBlock());
world.getServer().execute(() -> PORTAL_MANAGER.onPortalBlockBroken(world, pos, blockId));
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);
}
}
}
}
}

View File

@ -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();
}

View File

@ -42,7 +42,7 @@ public class DefaultLinkingSystem implements PortalLinkingSystem {
@Override
public void linkPortals(LinkedList<PortalRepresentation> portalRepresentations) {
LOGGER.info("start linkPortals");
LOGGER.debug("start linkPortals");
for (Node<PortalRepresentation> node = portalRepresentations.getNode(0); node != null; node = node.getNext()) {
PortalRepresentation portalRepresentation = node.getValue();
@ -53,7 +53,7 @@ public class DefaultLinkingSystem implements PortalLinkingSystem {
portalsAtPosition.add(finalNode);
});
}
LOGGER.info("end linkPortals");
LOGGER.debug("end linkPortals");
}
@Override
@ -72,7 +72,6 @@ public class DefaultLinkingSystem implements PortalLinkingSystem {
prevEntityPos = new Vec3d(entity.prevX, entity.prevY, entity.prevZ);
}
if (RaycastHelper.passedOnAxis(prevEntityPos, entity.getPos(), pos, axis)) {
//LOGGER.info("passed on Axis!");
if (onPortalPassed(entity, pos, world, axis)) {
return true;
}

View File

@ -10,6 +10,8 @@ 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;
@ -44,17 +46,17 @@ public class ImmersivePortalsLinkingSystem implements PortalLinkingSystem {
BlockBox linkedToPortalBlockBox = linkedToPortalRepresentation.location();
Box linkedToPortalBox = Box.from(linkedToPortalBlockBox);
List<qouteall.imm_ptl.core.portal.Portal> outgoingPortals = new ArrayList<>(getPortalList(fromPortalRepresentation));
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++) {
qouteall.imm_ptl.core.portal.Portal outgoingPortal = outgoingPortals.get(i);
outgoingPortal.kill();
Portal outgoingPortal = outgoingPortals.get(i);
PortalAPI.removeGlobalPortal(getPortalWorld(fromPortalRepresentation), outgoingPortal);
}
}
if (outgoingPortals.isEmpty()) {
qouteall.imm_ptl.core.portal.Portal portal = qouteall.imm_ptl.core.portal.Portal.ENTITY_TYPE.create(fromPortalWorld);
Portal portal = Portal.ENTITY_TYPE.create(fromPortalWorld);
if (portal == null) {
LOGGER.error("could not create PortalRepresentation entity for {}", fromPortalRepresentation);
return;
@ -72,25 +74,19 @@ public class ImmersivePortalsLinkingSystem implements PortalLinkingSystem {
);
portal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
outgoingPortals.add(portal);
if (!fromPortalWorld.spawnEntity(portal)) {
LOGGER.error("could not spawn PortalRepresentation entity for {}", fromPortalRepresentation);
return;
}
PortalAPI.addGlobalPortal(fromPortalWorld, portal);
}
if (outgoingPortals.size() == 1) {
qouteall.imm_ptl.core.portal.Portal portal = PortalAPI.createFlippedPortal(outgoingPortals.getFirst());
Portal portal = PortalAPI.createFlippedPortal(outgoingPortals.getFirst());
outgoingPortals.add(portal);
if (!fromPortalWorld.spawnEntity(portal)) {
LOGGER.error("could not spawn second PortalRepresentation entity for {}", fromPortalRepresentation);
return;
PortalAPI.addGlobalPortal(fromPortalWorld, portal);
}
}
for (qouteall.imm_ptl.core.portal.Portal outgoingPortal : outgoingPortals) {
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();
GlobalPortalStorage.get(getPortalWorld(fromPortalRepresentation)).onDataChanged();
}
}
@ -106,7 +102,7 @@ public class ImmersivePortalsLinkingSystem implements PortalLinkingSystem {
@Override
public void unLinkPortal(PortalRepresentation portalRepresentation) {
List<qouteall.imm_ptl.core.portal.Portal> portals = getPortalList(portalRepresentation);
List<Portal> portals = getPortalList(portalRepresentation);
portals.forEach(portalEntity -> PortalAPI.removeGlobalPortal(getPortalWorld(portalRepresentation), portalEntity));
}
@ -131,10 +127,12 @@ public class ImmersivePortalsLinkingSystem implements PortalLinkingSystem {
return false;
}
private List<qouteall.imm_ptl.core.portal.Portal> getPortalList(PortalRepresentation portalRepresentation) {
private List<Portal> getPortalList(PortalRepresentation portalRepresentation) {
Box portalBox = Box.from(portalRepresentation.location());
return getPortalWorld(portalRepresentation)
.getEntitiesByType(TypeFilter.instanceOf(qouteall.imm_ptl.core.portal.Portal.class), portalBox, e -> e.isAlive() && contains(portalBox, e.getBoundingBox()));
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) {

View File

@ -8,10 +8,8 @@ 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.*;
@ -230,6 +228,10 @@ public class PortalHelper {
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;

View File

@ -8,7 +8,6 @@ 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 org.ladysnake.cca.api.v3.component.Component;
import quimufu.colourful_portals.general_util.LinkedList;
import quimufu.colourful_portals.general_util.Node;
@ -21,13 +20,11 @@ import static quimufu.colourful_portals.ColourfulPortalsMod.*;
public class PortalListComponent implements Component {
public static final int CURRENT_VERSION = 1;
private final WorldProperties worldProperties;
HashMap<Identifier, LinkedList<PortalRepresentation>> portalsPerPortalBlock = new HashMap<>();
Identifier lastPortalLinkingSystem = Identifier.of(MOD_ID, "immersive_portals_linking_system");
public PortalListComponent(WorldProperties worldProperties) {
this.worldProperties = worldProperties;
public PortalListComponent() {
}
@Override
@ -40,7 +37,7 @@ public class PortalListComponent implements Component {
LOGGER.warn("portals component comes from a newer version! Hopefully backwards compatible. Proceeding");
}
if (tag.contains("linking_system")) {
lastPortalLinkingSystem = Identifier.tryParse(tag.getString("version"));
lastPortalLinkingSystem = Identifier.tryParse(tag.getString("linking_system"));
}
NbtCompound blocks = tag.getCompound("blocks");
for (String block : blocks.getKeys()) {
@ -116,7 +113,7 @@ public class PortalListComponent implements Component {
}
}
LOGGER.info("portals {}", tag);
LOGGER.debug("portals {}", tag);
}
@ -147,16 +144,12 @@ public class PortalListComponent implements Component {
}
public PortalRepresentation getNext(Identifier blockId, PortalRepresentation portalRepresentationWithDim) {
LOGGER.info("gn");
LinkedList<PortalRepresentation> portals = getPortals(blockId);
Node<PortalRepresentation> node;
if ((node = portals.getNodeOf(portalRepresentationWithDim)) == null) {
return portals.getFirst();
}
PortalRepresentation portalRepresentation = node.getNext() == null ? portals.getFirst() : node.getNext().getValue();
LOGGER.info("gne");
return portalRepresentation;
return node.getNext() == null ? portals.getFirst() : node.getNext().getValue();
}
public boolean containsPortal(Identifier blockId, PortalRepresentation portalRepresentation) {

View File

@ -15,7 +15,9 @@ import quimufu.colourful_portals.general_util.LinkedList;
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.*;
@ -24,6 +26,9 @@ public class PortalManager {
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;
}
@ -275,4 +280,20 @@ public class PortalManager {
}
}
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();
}
}
}

View File

@ -1,4 +1,6 @@
package quimufu.colourful_portals.portal;
import java.util.function.IntSupplier;
public record PrioritizedPortalLinkingSystemBuilder(PortalLinkingSystemBuilder portalLinkingSystemBuilder,
int priority) {}
IntSupplier priority) {}

View File

@ -13,7 +13,6 @@ public enum NullableAxis implements StringIdentifiable {
private final Direction.Axis axis;
NullableAxis(Direction.Axis axis) {
this.axis = axis;
}

View File

@ -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);
}
}

View File

@ -1,32 +0,0 @@
package quimufu.colourful_portals.util;
import net.minecraft.block.ShapeContext;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.Item;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape;
public class CollisionAwareShapeContext implements ShapeContext {
public CollisionAwareShapeContext() {
}
@Override
public boolean isDescending() {
return false;
}
@Override
public boolean isAbove(VoxelShape shape, BlockPos pos, boolean defaultValue) {
return false;
}
@Override
public boolean isHolding(Item item) {
return false;
}
@Override
public boolean canWalkOnFluid(FluidState stateAbove, FluidState state) {
return false;
}
}

View 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();
};
}
}

View File

@ -3,7 +3,6 @@ package quimufu.colourful_portals.util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import quimufu.colourful_portals.ColourfulPortalsMod;
import java.util.LinkedList;
import java.util.List;
@ -72,8 +71,6 @@ public class RaycastHelper {
return false;
}
//ColourfulPortalsMod.LOGGER.info("{}, {}: {} -> {}", hitAfter,axis, from, to);
return BlockPos.ofFloored(from.add(diff.multiply(hitAfter)))
.equals(blockPos);
}

View 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);
}
}
}

View File

@ -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());
}
}

View File

@ -1,28 +1,39 @@
{
"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.disableImmersivePortals": "Immersive Portals integration deaktivieren",
"colourful_portals.midnightconfig.explanation": "Diese Konfiguration erlaubt es, zusätzliche/andere Portalblocke zu definieren.\nPortale aus diesen verbinden sich mit anderen aus dem selben Block.\nDie 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.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 erstellen",
"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 erstellen",
"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",
"item.colourful_portals.portal_fluid_bucket": "Farbeimer",
"tag.block.c.crystals": "Kristalle",
"tag.block.c.slime_balls": "Schleimbälle"
"tag.block.c.slime_balls": "Schleimbälle",
"tag.block.c.crystals": "Kristalle"
}

View File

@ -6,7 +6,8 @@
"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.explanation": "This configuration allows you to define additional/other portal blocks.\nPortals from these connect to others from the same block.\nThe 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.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",
@ -20,6 +21,16 @@
"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",
"item.colourful_portals.portal_fluid_bucket": "Colourful Bucket",

View File

@ -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" }
]
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/black"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/blue"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/brown"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/cyan"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/gray"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/green"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/light_blue"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/light_gray"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/lime"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/magenta"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/orange"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/pink"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/purple"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/red"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/white"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_air_bottle/yellow"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "colourful_portals:item/colourful_pearl"
}
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View File

@ -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"
]
}

View 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
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Some files were not shown because too many files have changed in this diff Show More