update for 1.21, drop hard dependency on immersive portals

This commit is contained in:
QuImUfu 2024-06-25 22:40:27 +02:00
parent b1cd170c60
commit 4aa2643a76
31 changed files with 1514 additions and 380 deletions

View File

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

View File

@ -1,6 +1,6 @@
plugins {
id "com.modrinth.minotaur" version "2.+"
id 'fabric-loom' version '1.5-SNAPSHOT'
id 'fabric-loom' version '1.6-SNAPSHOT'
id 'maven-publish'
}
@ -54,22 +54,29 @@ dependencies {
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
modImplementation ("com.github.iPortalTeam:ImmersivePortalsMod:${project.immersive_portals_version}")
include(modApi("dev.onyxstudios.cardinal-components-api:cardinal-components-base:${project.cardinal_components_version}"))
modCompileOnly ("com.github.iPortalTeam:ImmersivePortalsMod:${project.immersive_portals_version}")
// Replace modImplementation with modApi if you expose components in your own API
include(modImplementation("dev.onyxstudios.cardinal-components-api:cardinal-components-level:${project.cardinal_components_version}"))
modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}"
// Includes Cardinal Components API as a Jar-in-Jar dependency (optional but recommended)
include "org.ladysnake.cardinal-components-api:cardinal-components-base:${project.cca_version}"
include(modApi(platform("de.siphalor.tweed4:tweed4-bom-$project.minecraft_version_major:$project.tweed_version")))
// Replace modImplementation with modApi if you expose components in your own API
modImplementation "org.ladysnake.cardinal-components-api:cardinal-components-level:${project.cca_version}"
// Includes Cardinal Components API as a Jar-in-Jar dependency (optional but recommended)
include "org.ladysnake.cardinal-components-api:cardinal-components-level:${project.cca_version}"
//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-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"))
//include(modApi("de.siphalor.tweed4:tweed4-tailor-screen-$project.minecraft_version_major"))
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")
@ -95,7 +102,7 @@ processResources {
}
tasks.withType(JavaCompile).configureEach {
it.options.release = 17
it.options.release = 21
}
java {
@ -104,8 +111,8 @@ java {
// If you remove this line, sources will not be generated.
withSourcesJar()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
jar {
@ -119,10 +126,10 @@ import com.modrinth.minotaur.dependencies.ModDependency
modrinth {
projectId = 'QXA901PE' // The ID of your Modrinth project. Slugs will not work.
uploadFile = remapJar // Tells Minotaur to use the remapped jar
versionType = "beta"
versionType = "alpha"
dependencies = [
new ModDependency('P7dR8mSH', 'required'), //required dependency on Fabric API
new ModDependency('zJpHMkdD', 'required') //required dependency on Immersive Portals
new ModDependency('dNHeFokL', 'optional'), //compatible with Sodium
]
}
// configure the maven publication

View File

@ -4,21 +4,22 @@ org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.20.4
minecraft_version_major=1.20
yarn_mappings=1.20.4+build.3
loader_version=0.15.7
minecraft_version=1.21
minecraft_version_major=1.21
yarn_mappings=1.21+build.2
loader_version=0.15.11
# Mod Properties
mod_version=0.9.3.1
maven_group=quimufu.colourful-portals
archives_base_name=colourful-portals
mod_version=0.9.4
maven_group=quimufu.colourful-portalRepresentations
archives_base_name=colourful-portalRepresentations
# Dependencies
fabric_version=0.96.11+1.20.4
cardinal_components_version=5.4.0
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.20.4-0.5.8
cloth_config_version=13.0.121
sodium_version=mc1.21-0.5.9
cloth_config_version=15.0.127
midnightlib_version=1.5.7-fabric

View File

@ -1,6 +1,6 @@
#Sun Jun 16 00:12:45 CEST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,16 +1,19 @@
package quimufu.colourful_portals;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroupEntries;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.enums.Instrument;
import net.minecraft.block.enums.NoteBlockInstrument;
import net.minecraft.entity.EntityType;
import net.minecraft.item.*;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.util.Identifier;
import net.minecraft.util.Rarity;
@ -19,25 +22,34 @@ import net.minecraft.world.BlockView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import quimufu.colourful_portals.config.ColourfulPortalConfig;
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 static quimufu.colourful_portals.Components.PORTAL_CANDIDATE_LIST;
import static quimufu.colourful_portals.Components.PORTAL_LIST;
public class ColourfulPortalsMod implements ModInitializer {
public static final String MOD_ID = "colourful_portals";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static final HashSet<Identifier> PORTAL_BLOCKS = new HashSet<>();
public static final PortalBlock PORTAL_BLOCK = new PortalBlock(FabricBlockSettings.create().instrument(Instrument.HAT).sounds(BlockSoundGroup.GLASS).strength(-1.0f, 3600000.8f).dropsNothing().nonOpaque().luminance(15).allowsSpawning(ColourfulPortalsMod::never).solidBlock(ColourfulPortalsMod::never).suffocates(ColourfulPortalsMod::never).blockVision(ColourfulPortalsMod::never).ticksRandomly());
public static final BlockItem PORTAL_BLOCK_ITEM = new BlockItem(PORTAL_BLOCK, new FabricItemSettings().rarity(Rarity.EPIC));
public static final Item BLOB_DARK = new Item(new FabricItemSettings());
public static final Item BLOB_BRIGHT = new Item(new FabricItemSettings());
public static final 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 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 PortalFluid PORTAL_FLUID = new PortalFluid();
public static final PortalFluidBlock PORTAL_FLUID_BLOCk = new PortalFluidBlock(PORTAL_FLUID, FabricBlockSettings.create().sounds(BlockSoundGroup.INTENTIONALLY_EMPTY).luminance(15).noCollision().strength(100.0f).dropsNothing());
public static final BucketItem PORTAL_FLUID_BUCKET_ITEM = new PortalFluidBucketItem(PORTAL_FLUID, new FabricItemSettings().recipeRemainder(Items.BUCKET).maxCount(1).rarity(Rarity.RARE));
public static final PortalFluidBlock PORTAL_FLUID_BLOCk = new PortalFluidBlock(PORTAL_FLUID, AbstractBlock.Settings.create().sounds(BlockSoundGroup.INTENTIONALLY_EMPTY).luminance((bs) -> 15).noCollision().strength(100.0f).dropsNothing());
public static final BucketItem PORTAL_FLUID_BUCKET_ITEM = new PortalFluidBucketItem(PORTAL_FLUID, new Item.Settings().recipeRemainder(Items.BUCKET).maxCount(1).rarity(Rarity.RARE));
public static PortalManager PORTAL_MANAGER;
public static final RegistryKey<Registry<PrioritizedPortalLinkingSystemBuilder>> PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY_KEY = RegistryKey.ofRegistry(Identifier.of(MOD_ID, "portal_linking_system"));
public static final Registry<PrioritizedPortalLinkingSystemBuilder> PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY = FabricRegistryBuilder.createSimple(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY_KEY).buildAndRegister();
private static boolean never(BlockState blockState, BlockView blockView, BlockPos blockPos, EntityType<?> entityType) {
return false;
@ -50,23 +62,28 @@ public class ColourfulPortalsMod implements ModInitializer {
@Override
public void onInitialize() {
LOGGER.info("Colourizing Portals...");
LOGGER.info("Colouring Portals...");
for (String id : ColourfulPortalConfig.portalBlocks.keySet()) {
for (String id : ColourfulPortalConfig.getAllPortalBlocks()) {
LOGGER.info(id);
PORTAL_BLOCKS.add(Identifier.tryParse(id));
}
if (!ColourfulPortalConfig.disableImmersivePortals && hasImmPtl()) {
Registry.register(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY, ImmersivePortalsLinkingSystem.IMMERSIVE_PORTALS_LINKING_SYSTEM, new PrioritizedPortalLinkingSystemBuilder(ImmersivePortalsLinkingSystem::new, 100));
}
Registry.register(PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY, DefaultLinkingSystem.DEFAULT_LINKING_SYSTEM, new PrioritizedPortalLinkingSystemBuilder(DefaultLinkingSystem::new, 90));
Identifier identifier = new Identifier(MOD_ID, "portal_block");
Identifier identifier = Identifier.of(MOD_ID, "portal_block");
Registry.register(Registries.BLOCK, identifier, PORTAL_BLOCK);
Registry.register(Registries.ITEM, identifier, PORTAL_BLOCK_ITEM);
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "portal_fluid_bucket"), PORTAL_FLUID_BUCKET_ITEM);
Registry.register(Registries.FLUID, new Identifier(MOD_ID, "portal_fluid"), PORTAL_FLUID);
Registry.register(Registries.BLOCK, new Identifier(MOD_ID, "portal_fluid_block"), PORTAL_FLUID_BLOCk);
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "portal_fluid_bucket"), PORTAL_FLUID_BUCKET_ITEM);
Registry.register(Registries.FLUID, Identifier.of(MOD_ID, "portal_fluid"), PORTAL_FLUID);
Registry.register(Registries.BLOCK, Identifier.of(MOD_ID, "portal_fluid_block"), PORTAL_FLUID_BLOCk);
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "colour_blob_bright"), BLOB_BRIGHT);
Registry.register(Registries.ITEM, new Identifier(MOD_ID, "colour_blob_dark"), BLOB_DARK);
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "colour_blob_bright"), BLOB_BRIGHT);
Registry.register(Registries.ITEM, Identifier.of(MOD_ID, "colour_blob_dark"), BLOB_DARK);
ItemGroupEvents.modifyEntriesEvent(ItemGroups.INGREDIENTS)
.register(ColourfulPortalsMod::addToIngredients);
@ -74,9 +91,51 @@ public class ColourfulPortalsMod implements ModInitializer {
ItemGroupEvents.modifyEntriesEvent(ItemGroups.TOOLS)
.register(ColourfulPortalsMod::addTtTools);
ServerLifecycleEvents.SERVER_STARTED.register(this::onServerStarted);
LOGGER.info("Portals Colourful!");
}
private boolean hasImmPtl() {
try {
Class.forName("qouteall.imm_ptl.core.IPModMain", false, this.getClass().getClassLoader());
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
private void onServerStarted(MinecraftServer minecraftServer) {
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(minecraftServer.getSaveProperties().getMainWorldProperties());
PortalListComponent portalList = PORTAL_LIST.get(minecraftServer.getSaveProperties().getMainWorldProperties());
PortalLinkingSystem currentLinkingSystem = getCurrentlyConfiguredLinkingSystem(minecraftServer);
if (currentLinkingSystem.getLinkingSystemId() != portalList.lastPortalLinkingSystemId() || currentLinkingSystem.needsReInit()) {
PrioritizedPortalLinkingSystemBuilder prevSystem;
if ((prevSystem = PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY.get(portalList.lastPortalLinkingSystemId())) != null) {
PortalLinkingSystem prevLS = prevSystem.portalLinkingSystemBuilder().build(minecraftServer);
for (Identifier blockId : portalList.getBlockIds()) {
for (PortalRepresentation portal : portalList.getPortals(blockId)) {
prevLS.unLinkPortal(portal);
}
}
}
for (Identifier blockId : portalList.getBlockIds()) {
currentLinkingSystem.linkPortals(portalList.getPortals(blockId));
}
}
PORTAL_MANAGER = new PortalManager(currentLinkingSystem, portalCandidateList, portalList);
}
private PortalLinkingSystem getCurrentlyConfiguredLinkingSystem(MinecraftServer minecraftServer) {
return PORTAL_LINKING_SYSTEM_BUILDER_REGISTRY
.stream()
.min(Comparator.comparing(PrioritizedPortalLinkingSystemBuilder::priority))
.map(PrioritizedPortalLinkingSystemBuilder::portalLinkingSystemBuilder)
.orElse(DefaultLinkingSystem::new)
.build(minecraftServer);
}
private static void addTtTools(FabricItemGroupEntries entries) {
entries.add(PORTAL_FLUID_BUCKET_ITEM);
}

View File

@ -1,17 +1,17 @@
package quimufu.colourful_portals;
import dev.onyxstudios.cca.api.v3.component.ComponentKey;
import dev.onyxstudios.cca.api.v3.component.ComponentRegistry;
import dev.onyxstudios.cca.api.v3.level.LevelComponentFactoryRegistry;
import dev.onyxstudios.cca.api.v3.level.LevelComponentInitializer;
import org.ladysnake.cca.api.v3.component.ComponentKey;
import org.ladysnake.cca.api.v3.component.ComponentRegistry;
import org.ladysnake.cca.api.v3.level.LevelComponentFactoryRegistry;
import org.ladysnake.cca.api.v3.level.LevelComponentInitializer;
import net.minecraft.util.Identifier;
import quimufu.colourful_portals.portal.PortalListComponent;
public class Components implements LevelComponentInitializer {
public static final ComponentKey<PortalListComponent> PORTAL_LIST =
ComponentRegistry.getOrCreate(new Identifier(ColourfulPortalsMod.MOD_ID, "portal_list"), PortalListComponent.class);
ComponentRegistry.getOrCreate(Identifier.of(ColourfulPortalsMod.MOD_ID, "portal_list"), PortalListComponent.class);
public static final ComponentKey<PortalListComponent> PORTAL_CANDIDATE_LIST =
ComponentRegistry.getOrCreate(new Identifier(ColourfulPortalsMod.MOD_ID, "portal_candidate_list"), PortalListComponent.class);
ComponentRegistry.getOrCreate(Identifier.of(ColourfulPortalsMod.MOD_ID, "portal_candidate_list"), PortalListComponent.class);
@Override
public void registerLevelComponentFactories(LevelComponentFactoryRegistry registry) {

View File

@ -4,6 +4,7 @@ import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.FluidFillable;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
@ -15,15 +16,16 @@ import net.minecraft.state.property.EnumProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.DyeColor;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.*;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
import quimufu.colourful_portals.portal.PortalManager;
import quimufu.colourful_portals.util.CollisionAwareShapeContext;
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_MANAGER;
public class PortalBlock extends Block implements FluidFillable {
@ -74,8 +76,58 @@ public class PortalBlock extends Block implements FluidFillable {
return VoxelShapes.empty();
}
@Override
protected void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) {
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);
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();
}
@ -127,7 +179,7 @@ public class PortalBlock extends Block implements FluidFillable {
}
return fluid == ColourfulPortalsMod.PORTAL_FLUID
&& (world instanceof ServerWorld)
&& PortalManager.canExtend((ServerWorld) world, pos);
&& PORTAL_MANAGER.canExtend((ServerWorld) world, pos);
}
@ -138,6 +190,6 @@ public class PortalBlock extends Block implements FluidFillable {
}
return fluidState.isOf(ColourfulPortalsMod.PORTAL_FLUID)
&& (world instanceof ServerWorld)
&& PortalManager.extend((ServerWorld) world, pos);
&& PORTAL_MANAGER.extend((ServerWorld) world, pos);
}
}

View File

@ -17,7 +17,7 @@ import quimufu.colourful_portals.ColourfulPortalsMod;
@Environment(value = EnvType.CLIENT)
public class PortalFluidRenderHandler implements FluidRenderHandler, CommonPortalFluidRenderer.VertexEater {
Identifier PORTAL_FLUID_STILL = new Identifier(ColourfulPortalsMod.MOD_ID, "block/portal_still");
Identifier PORTAL_FLUID_STILL = Identifier.of(ColourfulPortalsMod.MOD_ID, "block/portal_still");
private final CommonPortalFluidRenderer commonPortalFluidRenderer = new CommonPortalFluidRenderer();
private Sprite sprite = null;
@ -82,8 +82,7 @@ public class PortalFluidRenderHandler implements FluidRenderHandler, CommonPorta
.color(1f, 1f, 1f, 1.0f)
.texture(v[3], v[4])
.light(16)
.normal(0.0f, 1.0f, 0.0f)
.next();
.normal(0.0f, 1.0f, 0.0f);
}

View File

@ -9,7 +9,9 @@ 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;

View File

@ -1,44 +1,131 @@
package quimufu.colourful_portals.config;
import com.google.common.base.CaseFormat;
import com.google.common.collect.ImmutableMap;
import de.siphalor.tweed4.annotated.AConfigEntry;
import de.siphalor.tweed4.annotated.ATweedConfig;
import de.siphalor.tweed4.config.ConfigEnvironment;
import de.siphalor.tweed4.config.ConfigScope;
//import de.siphalor.tweed4.tailor.cloth.ClothData;
import com.google.common.collect.Lists;
import eu.midnightdust.lib.config.MidnightConfig;
import net.minecraft.util.DyeColor;
//import quimufu.colourful_portals.ColourfulPortalsMod;
import java.util.Map;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ATweedConfig(serializer = "tweed4:hjson", scope = ConfigScope.GAME, environment = ConfigEnvironment.SERVER, tailors = {"tweed4:lang_json_descriptions", "tweed4:coat", "tweed4:json_schema"}, casing = CaseFormat.LOWER_HYPHEN)
//@ClothData(modid = ColourfulPortalsMod.MOD_ID)
public class ColourfulPortalConfig {
public class ColourfulPortalConfig extends MidnightConfig {
@AConfigEntry(comment =
"""
Allows you to add additional blocks as portalBlocks,
assigning them the color of the portal plane.
null causes the portals out of that block to have *no* plane.
Changes in the plane color will only apply to new portals,
or on portal update.
""")
public static Map<String, DyeColor> portalBlocks = ImmutableMap.ofEntries(
Map.entry("minecraft:white_wool", DyeColor.WHITE),
Map.entry("minecraft:orange_wool", DyeColor.ORANGE),
Map.entry("minecraft:magenta_wool", DyeColor.MAGENTA),
Map.entry("minecraft:light_blue_wool", DyeColor.LIGHT_BLUE),
Map.entry("minecraft:yellow_wool", DyeColor.YELLOW),
Map.entry("minecraft:lime_wool", DyeColor.LIME),
Map.entry("minecraft:pink_wool", DyeColor.PINK),
Map.entry("minecraft:gray_wool", DyeColor.GRAY),
Map.entry("minecraft:light_gray_wool", DyeColor.LIGHT_GRAY),
Map.entry("minecraft:cyan_wool", DyeColor.CYAN),
Map.entry("minecraft:purple_wool", DyeColor.PURPLE),
Map.entry("minecraft:blue_wool", DyeColor.BLUE),
Map.entry("minecraft:brown_wool", DyeColor.BROWN),
Map.entry("minecraft:green_wool", DyeColor.GREEN),
Map.entry("minecraft:red_wool", DyeColor.RED),
Map.entry("minecraft:black_wool", DyeColor.BLACK));
@Comment(category = "text")
public static Comment explanation;
@Entry(category = "text", name = "Disable Immersive Portals integration")
public static boolean disableImmersivePortals = false;
@Entry(category = "text", name = "Blocks that create white portals")
public static List<String> white = Lists.newArrayList("minecraft:white_wool");
@Entry(category = "text", name = "Blocks that create orange portals")
public static List<String> orange = Lists.newArrayList("minecraft:orange_wool");
@Entry(category = "text", name = "Blocks that create magenta portals")
public static List<String> magenta = Lists.newArrayList("minecraft:magenta_wool");
@Entry(category = "text", name = "Blocks that create light blue portals")
public static List<String> light_blue = Lists.newArrayList("minecraft:light_blue_wool");
@Entry(category = "text", name = "Blocks that create yellow portals")
public static List<String> yellow = Lists.newArrayList("minecraft:yellow_wool");
@Entry(category = "text", name = "Blocks that create lime portals")
public static List<String> lime = Lists.newArrayList("minecraft:lime_wool");
@Entry(category = "text", name = "Blocks that create pink portals")
public static List<String> pink = Lists.newArrayList("minecraft:pink_wool");
@Entry(category = "text", name = "Blocks that create gray portals")
public static List<String> gray = Lists.newArrayList("minecraft:gray_wool");
@Entry(category = "text", name = "Blocks that create light gray portals")
public static List<String> light_gray = Lists.newArrayList("minecraft:light_gray_wool");
@Entry(category = "text", name = "Blocks that create cyan portals")
public static List<String> cyan = Lists.newArrayList("minecraft:cyan_wool");
@Entry(category = "text", name = "Blocks that create purple portals")
public static List<String> purple = Lists.newArrayList("minecraft:purple_wool");
@Entry(category = "text", name = "Blocks that create blue portals")
public static List<String> blue = Lists.newArrayList("minecraft:blue_wool");
@Entry(category = "text", name = "Blocks that create brown portals")
public static List<String> brown = Lists.newArrayList("minecraft:brown_wool");
@Entry(category = "text", name = "Blocks that create green portals")
public static List<String> green = Lists.newArrayList("minecraft:green_wool");
@Entry(category = "text", name = "Blocks that create red portals")
public static List<String> red = Lists.newArrayList("minecraft:red_wool");
@Entry(category = "text", name = "Blocks that create black portals")
public static List<String> black = Lists.newArrayList("minecraft:black_wool");
@Entry(category = "text", name = "Blocks that create fully transparent portals")
public static List<String> none = Lists.newArrayList();
public static Set<String> getAllPortalBlocks() {
Set<String> allBlocks = new HashSet<>();
allBlocks.addAll(white);
allBlocks.addAll(orange);
allBlocks.addAll(magenta);
allBlocks.addAll(light_blue);
allBlocks.addAll(yellow);
allBlocks.addAll(lime);
allBlocks.addAll(pink);
allBlocks.addAll(gray);
allBlocks.addAll(light_gray);
allBlocks.addAll(cyan);
allBlocks.addAll(purple);
allBlocks.addAll(blue);
allBlocks.addAll(brown);
allBlocks.addAll(green);
allBlocks.addAll(red);
allBlocks.addAll(black);
allBlocks.addAll(none);
return allBlocks;
}
public static DyeColor colorOf(String block) {
if (white.contains(block)) {
return DyeColor.WHITE;
} else if (orange.contains(block)) {
return DyeColor.ORANGE;
} else if (magenta.contains(block)) {
return DyeColor.MAGENTA;
} else if (light_blue.contains(block)) {
return DyeColor.LIGHT_BLUE;
} else if (yellow.contains(block)) {
return DyeColor.YELLOW;
} else if (lime.contains(block)) {
return DyeColor.LIME;
} else if (pink.contains(block)) {
return DyeColor.PINK;
} else if (gray.contains(block)) {
return DyeColor.GRAY;
} else if (light_gray.contains(block)) {
return DyeColor.LIGHT_GRAY;
} else if (cyan.contains(block)) {
return DyeColor.CYAN;
} else if (purple.contains(block)) {
return DyeColor.PURPLE;
} else if (blue.contains(block)) {
return DyeColor.BLUE;
} else if (brown.contains(block)) {
return DyeColor.BROWN;
} else if (green.contains(block)) {
return DyeColor.GREEN;
} else if (red.contains(block)) {
return DyeColor.RED;
} else if (black.contains(block)) {
return DyeColor.BLACK;
} else if (none.contains(block)) {
return null; // Fully transparent portal
} else {
throw new IllegalArgumentException("Invalid portal block: " + block);
}
}
}

View File

@ -0,0 +1,458 @@
package quimufu.colourful_portals.general_util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
public class LinkedList<E> implements List<E> {
Node<E> headNode = null;
@Override
public boolean contains(Object o) {
return indexOf(o) != -1;
}
@Override
public int size() {
Node<E> node = headNode;
int size = 0;
while (node != null) {
size++;
node = node.getNext();
}
LOGGER.info("" + size);
return size;
}
@Override
public boolean isEmpty() {
return headNode == null;
}
@NotNull
@Override
public Iterator<E> iterator() {
return new LLIterator<>(this);
}
@NotNull
@Override
public Object[] toArray() {
Object[] objects = new Object[size()];
Node<E> node = headNode;
int index = 0;
while (node != null) {
objects[index] = node.getValue();
node = node.getNext();
index++;
}
return objects;
}
@NotNull
@Override
public <T> T @NotNull [] toArray(T[] a) {
int size = this.size();
if (size > a.length) {
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
}
Object[] result = a;
Node<E> node = headNode;
int index = 0;
while (node != null) {
result[index] = node.getValue();
node = node.getNext();
index++;
}
if (size != a.length) {
a[index] = null;
}
return a;
}
@Override
public boolean add(E e) {
if (headNode == null) {
headNode = new Node<>(e, this);
return true;
}
Node<E> node = getLastNode(headNode);
node.insertAfter(e);
return true;
}
private @NotNull Node<E> getLastNode(@NotNull Node<E> start) {
Node<E> node = start;
while (node.getNext() != null) {
node = node.getNext();
}
return node;
}
@Override
public boolean containsAll(@NotNull Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
@Override
public boolean addAll(@NotNull Collection<? extends E> c) {
if (headNode == null) {
headNode = new Node<>(null, this);
Node<E> startNode = new Node<>(null, this);
Node<E> currNode = startNode;
for (E o : c) {
currNode.insertAfter(o);
currNode = currNode.getNext();
}
startNode.remove();
return true;
}
Node<E> currNode = getLastNode(headNode);
for (E o : c) {
currNode.insertAfter(o);
currNode = currNode.getNext();
}
return true;
}
@Override
public boolean addAll(int index, @NotNull Collection<? extends E> c) {
if (c.isEmpty()) {
return false;
}
Node<E> node = getNode(index);
for (E e : c) {
node.insertBefore(e);
}
return true;
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
if (headNode == null) {
return false;
}
boolean ret = false;
Node<E> currNode = headNode;
while (currNode != null) {
if (c.contains(currNode.getValue())) {
currNode.remove();
ret = true;
}
currNode = currNode.getNext();
}
return ret;
}
@Override
public boolean retainAll(@NotNull Collection<?> c) {
if (headNode == null) {
return false;
}
boolean ret = false;
Node<E> currNode = headNode;
while (currNode != null) {
if (!c.contains(currNode.getValue())) {
currNode.remove();
ret = true;
}
currNode = currNode.getNext();
}
return ret;
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
Node<E> node = headNode;
while (node != null) {
if (Objects.equals(node.getValue(), o)) {
node.remove();
return true;
}
node = node.getNext();
}
return false;
}
public E get(int index) {
return getNode(index).getValue();
}
public Node<E> getNode(int index) {
Node<E> node = headNode;
for (int i = 0; i < index; i++) {
if (node == null)
throw new IndexOutOfBoundsException(index);
node = node.getNext();
}
return node;
}
public E set(int index, E element) {
getNode(index).setValue(element);
return element;
}
public void add(int index, E element) {
getNode(index).insertBefore(element);
}
public E remove(int index) {
Node<E> node = getNode(index);
node.remove();
return node.getValue();
}
public int indexOf(Object o) {
int index = 0;
Node<E> node = headNode;
while (node != null) {
if (Objects.equals(node.getValue(), o)) {
return index;
}
node = node.getNext();
index++;
}
return -1;
}
public int lastIndexOf(Object o) {
int ret = -1;
int index = 0;
Node<E> node = headNode;
while (node != null) {
if (Objects.equals(node.getValue(), o)) {
ret = index;
}
node = node.getNext();
index++;
}
return ret;
}
@NotNull
@Override
public ListIterator<E> listIterator() {
return new LLLIterator<>(this);
}
@NotNull
@Override
public ListIterator<E> listIterator(int index) {
LLLIterator<E> iterator = new LLLIterator<>(this);
iterator.nextNode = getNode(index);
iterator.previousNode = iterator.nextNode.getPrevious();
return iterator;
}
@NotNull
@Override
public List<E> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException();
}
@Nullable
public Node<E> getNodeOf(E content) {
Node<E> node = headNode;
while (node != null) {
if (Objects.equals(node.getValue(), content)) {
return node;
}
node = node.getNext();
}
return null;
}
private static class LLIterator<E> implements Iterator<E> {
private Node<E> nextNode;
public LLIterator(LinkedList<E> linkedList) {
nextNode = linkedList.headNode;
}
@Override
public boolean hasNext() {
return nextNode != null;
}
@Override
public E next() {
if (nextNode == null)
throw new IndexOutOfBoundsException();
E value = nextNode.getValue();
nextNode = nextNode.getNext();
return value;
}
}
private static class LLLIterator<E> implements ListIterator<E> {
@Nullable
private Node<E> nextNode;
@Nullable
private Node<E> previousNode;
private boolean lastNext = true;
private final LinkedList<E> linkedList;
public LLLIterator(LinkedList<E> linkedList) {
nextNode = linkedList.headNode;
previousNode = null;
this.linkedList = linkedList;
}
@Override
public boolean hasNext() {
return nextNode != null;
}
@Override
public E next() {
tryFixOrphans();
if (nextNode == null)
throw new IndexOutOfBoundsException();
E value = nextNode.getValue();
previousNode = nextNode;
nextNode = nextNode.getNext();
lastNext = true;
return value;
}
@Override
public boolean hasPrevious() {
return previousNode != null;
}
@Override
public E previous() {
tryFixOrphans();
if (previousNode == null)
throw new IndexOutOfBoundsException();
E value = previousNode.getValue();
nextNode = previousNode;
previousNode = previousNode.getPrevious();
lastNext = false;
return value;
}
@Override
public int nextIndex() {
tryFixOrphans();
if(nextNode == null)
return linkedList.size();
return linkedList.indexOfNode(nextNode);
}
@Override
public int previousIndex() {
tryFixOrphans();
if(previousNode == null) {
return -1;
}
return linkedList.indexOfNode(previousNode);
}
@Override
public void remove() {
tryFixOrphans();
if(lastNext) {
if(previousNode == null)
throw new IllegalStateException();
previousNode.remove();
previousNode = previousNode.getPrevious();
} else {
if(nextNode == null)
throw new IllegalStateException();
nextNode.remove();
nextNode = nextNode.getNext();
}
}
@Override
public void set(E e) {
tryFixOrphans();
if(lastNext) {
if(previousNode == null)
throw new IllegalStateException();
previousNode.setValue(e);
} else {
if(nextNode == null)
throw new IllegalStateException();
nextNode.setValue(e);
}
}
@Override
public void add(E e) {
tryFixOrphans();
if(nextNode != null) {
nextNode.insertBefore(e);
} else if(previousNode != null) {
previousNode.insertAfter(e);
} else {
// empty!
linkedList.add(e);
nextNode = linkedList.headNode;
}
}
private void tryFixOrphans() {
if (previousNode != null && previousNode.orphaned()){
if(nextNode != null && !nextNode.orphaned()){
previousNode = nextNode.getPrevious();
} else {
throw new ConcurrentModificationException("Node was concurrently deleted!");
}
}
if (nextNode != null && nextNode.orphaned()){
if(previousNode != null && !previousNode.orphaned()){
nextNode = previousNode.getNext();
} else {
throw new ConcurrentModificationException("Node was concurrently deleted!");
}
}
}
}
private int indexOfNode(Node<E> needle) {
int index = 0;
Node<E> node = headNode;
while (node != null) {
if (node == needle) {
return index;
}
node = node.getNext();
index++;
}
return -1;
}
}

View File

@ -0,0 +1,89 @@
package quimufu.colourful_portals.general_util;
import org.jetbrains.annotations.Nullable;
public class Node<E> {
private Node<E> next = null;
private Node<E> previous = null;
private E value;
public LinkedList<E> getPartOf() {
return partOf;
}
private final LinkedList<E> partOf;
private boolean orphaned = false;
protected Node(E value, LinkedList<E> partOf) {
this.value = value;
this.partOf = partOf;
}
public Node<E> getNext() {
return next;
}
public void setNext(@Nullable Node<E> next) {
if (next != null) {
next.previous = this;
}
if(next == this){
throw new IllegalArgumentException("cycles are not allowed");
}
this.next = next;
}
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
public Node<E> getPrevious() {
return previous;
}
public void setPrevious(@Nullable Node<E> previous) {
if (previous != null) {
previous.next = this;
}
this.previous = previous;
}
public void remove() {
if (partOf.headNode == this) {
if (next != null) {
partOf.headNode = next;
} else {
partOf.headNode = previous;
}
}
if (previous != null) {
previous.setNext(next);
}
if (next != null) {
next.setPrevious(previous);
}
orphaned = true;
}
public void insertBefore(E element) {
Node<E> newNode = new Node<>(element, partOf);
newNode.setPrevious(this.previous);
newNode.setNext(this);
}
public void insertAfter(E element) {
Node<E> newNode = new Node<>(element, partOf);
newNode.setNext(this.next);
newNode.setPrevious(this);
}
public boolean orphaned() {
return orphaned;
}
}

View File

@ -15,7 +15,7 @@ import quimufu.colourful_portals.client.AlphaInterpolationHolder;
import java.util.List;
@Mixin(targets = "net.minecraft.client.texture.SpriteContents$Animation")
@Mixin(SpriteContents.Animation.class)
public class AnimationMixin implements AlphaInterpolationHolder {
@Shadow
@Final

View File

@ -9,31 +9,29 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import quimufu.colourful_portals.portal.PortalManager;
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_BLOCKS;
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
@Mixin(ServerWorld.class)
public class BlockChangeMixin {
@Inject(at = @At("RETURN"), method = "onBlockChanged")
private void init(BlockPos pos, BlockState oldBlock, BlockState newBlock, CallbackInfo info) {
private void cpm_afterOnBlockChanged(BlockPos pos, BlockState oldBlock, BlockState newBlock, CallbackInfo info) {
ServerWorld world = (ServerWorld) (Object) this;
if(world.isDebugWorld()|| !world.isChunkLoaded(pos))
return;
// This code is injected into the end of ServerWorld.onBlockChanged()V
if (oldBlock.getBlock() != newBlock.getBlock()) {
if (PORTAL_BLOCKS.contains(Registries.BLOCK.getId(newBlock.getBlock()))) {
LOGGER.debug("onBlockNew {} -> {}", oldBlock, newBlock);
LOGGER.info("onBlockNew {} -> {}", oldBlock, newBlock);
Identifier blockId = Registries.BLOCK.getId(newBlock.getBlock());
PortalManager.onPortalBlockPlaced(world, pos, blockId);
PORTAL_MANAGER.onPortalBlockPlaced(world, pos, blockId);
}
if (PORTAL_BLOCKS.contains(Registries.BLOCK.getId(oldBlock.getBlock()))) {
LOGGER.debug("onBlockOld {} -> {}", oldBlock, newBlock);
LOGGER.info("onBlockOld {} -> {}", oldBlock, newBlock);
Identifier blockId = Registries.BLOCK.getId(oldBlock.getBlock());
PortalManager.onPortalBlockBroken(world, pos, blockId);
PORTAL_MANAGER.onPortalBlockBroken(world, pos, blockId);
}
}

View File

@ -0,0 +1,65 @@
package quimufu.colourful_portals.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import quimufu.colourful_portals.util.CollisionAwareShapeContext;
import java.util.HashSet;
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_BLOCK;
@Mixin(Entity.class)
public class EntityMixin {
@Shadow
private World world;
@Shadow
public double prevX;
@Shadow
public double prevY;
@Shadow
public double prevZ;
@Shadow
private Vec3d pos;
@Unique
private final ThreadLocal<HashSet<BlockPos>> calledAlready = ThreadLocal.withInitial(HashSet::new);
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;onEntityCollision(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/entity/Entity;)V"), method = "checkBlockCollision")
public void cpm_dontCallTwice(CallbackInfo ci, @Local BlockPos.Mutable mutable) {
calledAlready.get().add(mutable.toImmutable());
}
@Inject(at = @At("RETURN"), method = "checkBlockCollision")
public void cpm_afterCheckBlockCollision(CallbackInfo ci) {
if (!world.isClient()) {
BlockHitResult hitResult = world.raycast(new RaycastContext(new Vec3d(prevX, prevY, prevZ), pos, RaycastContext.ShapeType.COLLIDER, RaycastContext.FluidHandling.NONE, new CollisionAwareShapeContext()));
BlockPos hitResultBlockPos = hitResult.getBlockPos();
if (!calledAlready.get().contains(hitResultBlockPos)) {
BlockState state = world.getBlockState(hitResultBlockPos);
if (state.getBlock() == PORTAL_BLOCK) {
state.onEntityCollision(world, hitResultBlockPos, (Entity) ((Object) this));
}
}
calledAlready.get().clear();
}
}
}

View File

@ -0,0 +1,115 @@
package quimufu.colourful_portals.portal;
import net.minecraft.entity.Entity;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.TeleportTarget;
import quimufu.colourful_portals.general_util.LinkedList;
import quimufu.colourful_portals.general_util.Node;
import java.util.*;
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
import static quimufu.colourful_portals.ColourfulPortalsMod.MOD_ID;
import static quimufu.colourful_portals.portal.PortalHelper.getAxisW;
import static quimufu.colourful_portals.portal.PortalHelper.insideOf;
import static quimufu.colourful_portals.portal.PortalManager.getDimId;
public class DefaultLinkingSystem implements PortalLinkingSystem {
public static final Identifier DEFAULT_LINKING_SYSTEM = Identifier.of(MOD_ID, "default_linking_system");
private final Map<BlockPos, Set<Node<PortalRepresentation>>> positionalLookup = new HashMap<>();
private final MinecraftServer server;
public DefaultLinkingSystem(MinecraftServer minecraftServer) {
this.server = minecraftServer;
}
@Override
public Identifier getLinkingSystemId() {
return DEFAULT_LINKING_SYSTEM;
}
@Override
public void linkPortals(LinkedList<PortalRepresentation> portalRepresentations) {
LOGGER.info("start linkPortals");
for (Node<PortalRepresentation> node = portalRepresentations.getNode(0); node != null; node = node.getNext()) {
PortalRepresentation portalRepresentation = node.getValue();
Node<PortalRepresentation> finalNode = node;
insideOf(portalRepresentation.location())
.forEachRemaining(blockPos -> {
Set<Node<PortalRepresentation>> portalsAtPosition = positionalLookup.computeIfAbsent(blockPos, (k) -> new HashSet<>(1));
portalsAtPosition.add(finalNode);
});
}
LOGGER.info("end linkPortals");
}
@Override
public void unLinkPortal(PortalRepresentation portalRepresentation) {
//no-op
}
@Override
public void onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a) {
Set<Node<PortalRepresentation>> portalRepresentationCandidated = positionalLookup.get(pos);
Optional<Node<PortalRepresentation>> portalOpt = portalRepresentationCandidated.stream()
.filter(n -> !n.orphaned())
.filter(n -> n.getValue() != null)
.filter(n -> getAxisW(n.getValue().location()).rotateYClockwise().getAxis() == a)
.filter(n -> Objects.equals(n.getValue().dimensionId(), getDimId(world)))
.findAny();
if (portalOpt.isPresent()) {
Node<PortalRepresentation> fromNode = portalOpt.get();
Node<PortalRepresentation> toNode = fromNode.getNext() != null ? fromNode.getNext() : fromNode.getPartOf().getNode(0);
if (toNode == null || toNode.getValue() == null) {
return;
}
PortalRepresentation fromPortal = fromNode.getValue();
PortalRepresentation toPortal = toNode.getValue();
BlockBox fromBox = fromPortal.location();
Vec3d fromCenter = new Vec3d((fromBox.getMaxX()+1 + fromBox.getMinX())/2D,(fromBox.getMaxY()+1 + fromBox.getMinY())/2D,(fromBox.getMaxZ()+1 + fromBox.getMinZ())/2D);
BlockBox toBox = toPortal.location();
Vec3d toCenter = new Vec3d((toBox.getMaxX()+1 + toBox.getMinX())/2D,(toBox.getMaxY()+1 + toBox.getMinY())/2D,(toBox.getMaxZ()+1 + toBox.getMinZ())/2D);
if (getAxisW(fromPortal.location()) == getAxisW(toPortal.location())) {
Vec3d relPos = entity.getPos().subtract(fromCenter);
Vec3d targetPos = toCenter.add(relPos);
//todo: maybe fancy continoous movement math!
ServerWorld toWorld = getPortalWorld(toPortal);
TeleportTarget teleportTarget = new TeleportTarget(toWorld, targetPos, entity.getVelocity(), entity.getYaw(), entity.getPitch(), TeleportTarget.ADD_PORTAL_CHUNK_TICKET);
entity.teleportTo(teleportTarget);
} else {
Vec3d relPos = entity.getPos().subtract(fromCenter);
Vec3d targetPos = toCenter.add(new Vec3d(-relPos.z, relPos.y, relPos.x));
//todo: maybe fancy continoous movement math!
ServerWorld toWorld = getPortalWorld(toPortal);
TeleportTarget teleportTarget = new TeleportTarget(toWorld, targetPos, entity.getVelocity(), (entity.getYaw() + 90.F) % 360.F, entity.getPitch(), TeleportTarget.ADD_PORTAL_CHUNK_TICKET);
entity.teleportTo(teleportTarget);
}
}
}
@Override
public boolean needsReInit() {
return true;
}
private ServerWorld getPortalWorld(PortalRepresentation fromPortalRepresentation) {
ServerWorld serverWorld = server.getWorld(RegistryKey.of(RegistryKeys.WORLD, fromPortalRepresentation.dimensionId()));
if (serverWorld == null) {
LOGGER.warn("couldn't get portal dimensionId for portal {}. Don't sue me!", fromPortalRepresentation);
throw new RuntimeException();
}
return serverWorld;
}
}

View File

@ -0,0 +1,141 @@
package quimufu.colourful_portals.portal;
import net.minecraft.entity.Entity;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypeFilter;
import net.minecraft.util.math.*;
import net.minecraft.world.World;
import qouteall.imm_ptl.core.api.PortalAPI;
import qouteall.q_misc_util.my_util.DQuaternion;
import quimufu.colourful_portals.general_util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
import static quimufu.colourful_portals.ColourfulPortalsMod.MOD_ID;
public class ImmersivePortalsLinkingSystem implements PortalLinkingSystem {
public static final Identifier IMMERSIVE_PORTALS_LINKING_SYSTEM = Identifier.of(MOD_ID, "immersive_portals_linking_system");
private final MinecraftServer server;
public ImmersivePortalsLinkingSystem(MinecraftServer server) {
this.server = server;
}
@Override
public Identifier getLinkingSystemId() {
return IMMERSIVE_PORTALS_LINKING_SYSTEM;
}
public void assureLinked(PortalRepresentation fromPortalRepresentation, PortalRepresentation linkedToPortalRepresentation) {
ServerWorld fromPortalWorld = getPortalWorld(fromPortalRepresentation);
RegistryKey<World> linkedToPortalWorldRegKey = RegistryKey.of(RegistryKeys.WORLD, linkedToPortalRepresentation.dimensionId());
BlockBox fromPortalBlockBox = fromPortalRepresentation.location();
Box fromPortalBox = Box.from(fromPortalBlockBox);
BlockBox linkedToPortalBlockBox = linkedToPortalRepresentation.location();
Box linkedToPortalBox = Box.from(linkedToPortalBlockBox);
List<qouteall.imm_ptl.core.portal.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();
}
}
if (outgoingPortals.isEmpty()) {
qouteall.imm_ptl.core.portal.Portal portal = qouteall.imm_ptl.core.portal.Portal.ENTITY_TYPE.create(fromPortalWorld);
if (portal == null) {
LOGGER.error("could not create PortalRepresentation entity for {}", fromPortalRepresentation);
return;
}
portal.setOriginPos(fromPortalBox.getCenter());
portal.setDestinationDimension(linkedToPortalWorldRegKey);
portal.setDestination(linkedToPortalBox.getCenter());
Vec3d axisW = Vec3d.of(PortalHelper.getAxisW(fromPortalBlockBox).getVector());
portal.setOrientationAndSize(
axisW, // axisW
new Vec3d(0, 1, 0), // axisH
2, // width
3 // height
);
portal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
outgoingPortals.add(portal);
if (!fromPortalWorld.spawnEntity(portal)) {
LOGGER.error("could not spawn PortalRepresentation entity for {}", fromPortalRepresentation);
return;
}
}
if (outgoingPortals.size() == 1) {
qouteall.imm_ptl.core.portal.Portal portal = PortalAPI.createFlippedPortal(outgoingPortals.getFirst());
outgoingPortals.add(portal);
if (!fromPortalWorld.spawnEntity(portal)) {
LOGGER.error("could not spawn second PortalRepresentation entity for {}", fromPortalRepresentation);
return;
}
}
for (qouteall.imm_ptl.core.portal.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();
}
}
@Override
public void linkPortals(LinkedList<PortalRepresentation> portalRepresentations) {
for (int i = 0; i < portalRepresentations.size(); i++) {
PortalRepresentation portalRepresentation = portalRepresentations.get(i);
PortalRepresentation linkedToPortalRepresentation = i + 1 < portalRepresentations.size() ? portalRepresentations.get(i + 1) : portalRepresentations.getFirst();
assureLinked(portalRepresentation, linkedToPortalRepresentation);
}
}
@Override
public void unLinkPortal(PortalRepresentation portalRepresentation) {
List<qouteall.imm_ptl.core.portal.Portal> portals = getPortalList(portalRepresentation);
portals.forEach(portalEntity -> PortalAPI.removeGlobalPortal(getPortalWorld(portalRepresentation), portalEntity));
}
@Override
public void onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a) {
//no-op, handled by immptl
}
@Override
public boolean needsReInit() {
return false;
}
private List<qouteall.imm_ptl.core.portal.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()));
}
private boolean contains(Box outside, Box inside) {
return outside.contains(inside.getMaxPos()) && outside.contains(inside.getMinPos());
}
private ServerWorld getPortalWorld(PortalRepresentation fromPortalRepresentation) {
ServerWorld serverWorld = server.getWorld(RegistryKey.of(RegistryKeys.WORLD, fromPortalRepresentation.dimensionId()));
if (serverWorld == null) {
LOGGER.warn("couldn't get portal dimensionId for portal {}. Don't sue me!", fromPortalRepresentation);
throw new RuntimeException();
}
return serverWorld;
}
}

View File

@ -0,0 +1,22 @@
package quimufu.colourful_portals.portal;
import net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import quimufu.colourful_portals.general_util.LinkedList;
import java.util.List;
public interface PortalLinkingSystem {
Identifier getLinkingSystemId();
void linkPortals(LinkedList<PortalRepresentation> portalRepresentations);
void unLinkPortal(PortalRepresentation portalRepresentation);
void onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a);
boolean needsReInit();
}

View File

@ -0,0 +1,8 @@
package quimufu.colourful_portals.portal;
import net.minecraft.server.MinecraftServer;
@FunctionalInterface
public interface PortalLinkingSystemBuilder {
PortalLinkingSystem build(MinecraftServer minecraftServer);
}

View File

@ -1,67 +1,113 @@
package quimufu.colourful_portals.portal;
import dev.onyxstudios.cca.api.v3.component.Component;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.nbt.NbtList;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldProperties;
import quimufu.colourful_portals.util.Pair;
import org.ladysnake.cca.api.v3.component.Component;
import quimufu.colourful_portals.general_util.LinkedList;
import quimufu.colourful_portals.general_util.Node;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
public class PortalListComponent implements Component {
public static final int CURRENT_VERSION = 1;
private final WorldProperties worldProperties;
HashMap<Identifier, List<Pair<BlockBox, Identifier>>> portalsPerPortalBlock = new HashMap<>();
HashMap<Identifier, LinkedList<PortalRepresentation>> portalsPerPortalBlock = new HashMap<>();
Identifier lastPortalLinkingSystem = Identifier.of(MOD_ID, "immersive_portals_linking_system");
public PortalListComponent(WorldProperties worldProperties) {
this.worldProperties = worldProperties;
}
@Override
public void readFromNbt(NbtCompound tag) {
public void readFromNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
if (tag != null && !tag.isEmpty()) {
for (String block : tag.getKeys()) {
NbtList portalsWithDim = tag.getList(block, NbtElement.COMPOUND_TYPE);
ArrayList<Pair<BlockBox, Identifier>> portals = new ArrayList<>();
if (!tag.contains("version")) {
readFromOldNbt(tag);
}
if (tag.getInt("version") > CURRENT_VERSION) {
LOGGER.warn("portals component comes from a newer version! Hopefully backwards compatible. Proceeding");
}
if (tag.contains("linking_system")) {
lastPortalLinkingSystem = Identifier.tryParse(tag.getString("version"));
}
NbtCompound blocks = tag.getCompound("blocks");
for (String block : blocks.getKeys()) {
NbtList portalsWithDim = blocks.getList(block, NbtElement.COMPOUND_TYPE);
LinkedList<PortalRepresentation> portalRepresentations = new LinkedList<>();
for (int i = 0; i < portalsWithDim.size(); i++) {
NbtCompound portalWithDim = portalsWithDim.getCompound(i);
NbtCompound portalCompound = portalWithDim.getCompound("portal");
BlockBox portal = BlockBox.create(NbtHelper.toBlockPos(portalCompound.getCompound("from")),
NbtHelper.toBlockPos(portalCompound.getCompound("to")));
BlockBox portal = BlockBox.create(NbtHelper.toBlockPos(portalCompound, "from").orElseThrow(),
NbtHelper.toBlockPos(portalCompound, "to").orElseThrow());
Identifier dimension = Identifier.tryParse(portalWithDim.getString("dim"));
portals.add(Pair.of(portal, dimension));
portalRepresentations.add(new PortalRepresentation(portal, dimension));
}
portalsPerPortalBlock.put(Identifier.tryParse(block), portals);
portalsPerPortalBlock.put(Identifier.tryParse(block), portalRepresentations);
}
}
}
private void readFromOldNbt(NbtCompound tag) {
for (String block : tag.getKeys()) {
NbtList portalsWithDim = tag.getList(block, NbtElement.COMPOUND_TYPE);
LinkedList<PortalRepresentation> portalRepresentations = new LinkedList<>();
for (int i = 0; i < portalsWithDim.size(); i++) {
NbtCompound portalWithDim = portalsWithDim.getCompound(i);
NbtCompound portalCompound = portalWithDim.getCompound("portal");
BlockBox portal = BlockBox.create(toBlockPosOld(portalCompound.getCompound("from")),
toBlockPosOld(portalCompound.getCompound("to")));
Identifier dimension = Identifier.tryParse(portalWithDim.getString("dim"));
portalRepresentations.add(new PortalRepresentation(portal, dimension));
}
portalsPerPortalBlock.put(Identifier.tryParse(block), portalRepresentations);
}
}
public static BlockPos toBlockPosOld(NbtCompound nbt) {
return new BlockPos(nbt.getInt("X"), nbt.getInt("Y"), nbt.getInt("Z"));
}
@Override
public void writeToNbt(NbtCompound tag) {
public void writeToNbt(NbtCompound tag, RegistryWrapper.WrapperLookup registryLookup) {
LOGGER.debug("save");
if(PORTAL_MANAGER == null) {
return;
}
tag.putInt("version", CURRENT_VERSION);
tag.putString("linking_system", PORTAL_MANAGER.getLinkingSystem().getLinkingSystemId().toString());
NbtCompound blocks = new NbtCompound();
tag.put("blocks", blocks);
for (Identifier portalBlockId : portalsPerPortalBlock.keySet()) {
NbtList portalsWithDimList = new NbtList();
tag.put(portalBlockId.toString(), portalsWithDimList);
for (Pair<BlockBox, Identifier> portalWithDim : portalsPerPortalBlock.get(portalBlockId)) {
blocks.put(portalBlockId.toString(), portalsWithDimList);
for (PortalRepresentation portalRepresentationWithDim : portalsPerPortalBlock.get(portalBlockId)) {
NbtCompound portalWithDimCompound = new NbtCompound();
portalsWithDimList.add(portalWithDimCompound);
NbtCompound portalCompound = new NbtCompound();
portalWithDimCompound.put("portal", portalCompound);
portalWithDimCompound.putString("dim", portalWithDim.second.toString());
portalWithDimCompound.putString("dim", portalRepresentationWithDim.dimensionId().toString());
BlockBox portal = portalWithDim.first;
BlockBox portal = portalRepresentationWithDim.location();
BlockPos from = new BlockPos(portal.getMinX(), portal.getMinY(), portal.getMinZ());
BlockPos to = new BlockPos(portal.getMaxX(), portal.getMaxY(), portal.getMaxZ());
@ -70,59 +116,58 @@ public class PortalListComponent implements Component {
}
}
LOGGER.debug("portals {}", tag.toString());
LOGGER.info("portals {}", tag);
}
public List<BlockBox> getContainingPortals(Identifier blockId, BlockPos pos, Identifier dim) {
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>()).stream()
.filter((portalWithDim) -> (portalWithDim.second.equals(dim)
&& portalWithDim.first.contains(pos)))
.map((p) -> p.first)
public List<PortalRepresentation> getContainingPortals(Identifier blockId, BlockPos pos, Identifier dim) {
return getPortals(blockId).stream()
.filter((portal) -> (portal.dimensionId().equals(dim) && portal.location().contains(pos)))
.toList();
}
public void createPortal(Identifier blockId, Pair<BlockBox, Identifier> portalWithDim) {
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
if (!portals.contains(portalWithDim)) {
portals.add(portalWithDim);
public void createPortal(Identifier blockId, PortalRepresentation portal) {
LinkedList<PortalRepresentation> portalRepresentations = getPortals(blockId);
if (!portalRepresentations.contains(portal)) {
portalRepresentations.add(portal);
}
}
public void removePortal(Identifier blockId, Pair<BlockBox, Identifier> portalWithDim) {
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
portals.remove(portalWithDim);
public void removePortal(Identifier blockId, PortalRepresentation portal) {
getPortals(blockId).remove(portal);
}
public List<Pair<BlockBox, Identifier>> getPortals(Identifier blockId) {
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
public LinkedList<PortalRepresentation> getPortals(Identifier blockId) {
return portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new LinkedList<>());
}
public Set<Identifier> getBlockIds() {
return portalsPerPortalBlock.keySet();
}
public Pair<BlockBox, Identifier> getNext(Identifier blockId, BlockBox portal, Identifier dim) {
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal, dim);
public PortalRepresentation getNext(Identifier blockId, PortalRepresentation portalRepresentationWithDim) {
LOGGER.info("gn");
LinkedList<PortalRepresentation> portals = getPortals(blockId);
Node<PortalRepresentation> node;
if ((node = portals.getNodeOf(portalRepresentationWithDim)) == null) {
int nextIndex = portals.indexOf(portalWithDim) + 1;
if (nextIndex >= portals.size()) {
return portals.get(0);
return portals.getFirst();
}
PortalRepresentation portalRepresentation = node.getNext() == null ? portals.getFirst() : node.getNext().getValue();
LOGGER.info("gne");
return portalRepresentation;
}
return portals.get(nextIndex);
public boolean containsPortal(Identifier blockId, PortalRepresentation portalRepresentation) {
return getPortals(blockId).contains(portalRepresentation);
}
public boolean containsPortal(Identifier blockId, Pair<BlockBox,Identifier> portal) {
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
return portals.contains(portal);
public PortalRepresentation getLast(Identifier blockId) {
return getPortals(blockId).getLast();
}
public Pair<BlockBox, Identifier> getLast(Identifier blockId) {
List<Pair<BlockBox, Identifier>> portals = portalsPerPortalBlock.computeIfAbsent(blockId, (i) -> new ArrayList<>());
return portals.get(portals.size()-1);
public Identifier lastPortalLinkingSystemId() {
return lastPortalLinkingSystem;
}
}

View File

@ -9,41 +9,50 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.DyeColor;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypeFilter;
import net.minecraft.util.math.*;
import net.minecraft.world.World;
import net.minecraft.util.math.BlockBox;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.Nullable;
import qouteall.imm_ptl.core.api.PortalAPI;
import qouteall.imm_ptl.core.portal.Portal;
import qouteall.q_misc_util.my_util.DQuaternion;
import quimufu.colourful_portals.config.ColourfulPortalConfig;
import quimufu.colourful_portals.util.Pair;
import quimufu.colourful_portals.general_util.LinkedList;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static quimufu.colourful_portals.ColourfulPortalsMod.*;
import static quimufu.colourful_portals.Components.PORTAL_CANDIDATE_LIST;
import static quimufu.colourful_portals.Components.PORTAL_LIST;
public class PortalManager {
public static void onPortalBlockPlaced(ServerWorld world, BlockPos pos, Identifier blockId) {
private final PortalListComponent portalCandidateList;
private final PortalListComponent portalList;
public PortalLinkingSystem getLinkingSystem() {
return linkingSystem;
}
private final PortalLinkingSystem linkingSystem;
public PortalManager(PortalLinkingSystem portalLinkingSystem, PortalListComponent portalCandidateList, PortalListComponent portalList) {
this.portalCandidateList = portalCandidateList;
this.portalList = portalList;
this.linkingSystem = portalLinkingSystem;
}
public void onPortalBlockPlaced(ServerWorld world, BlockPos pos, Identifier blockId) {
world.getProfiler().push("onPortalBlockPlaced");
LOGGER.debug("onPortalBlockPlaced, {}", blockId);
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
//delete portalCandidates obstructed by PortalBlocks for consistency
List<BlockBox> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, world.getDimensionKey().getValue());
LOGGER.debug("got containing PortalCandidates , {}", portalCandidates);
for (BlockBox portalCandidate : portalCandidates) {
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
LOGGER.debug("invalid, {}", portalCandidate);
Pair<BlockBox, Identifier> portalWithDim =
Pair.of(portalCandidate, world.getDimensionKey().getValue());
Identifier dimIdentifier = getDimId(world);
portalCandidateList.removePortal(blockId, portalWithDim);
//delete portalCandidates obstructed by PortalBlocks for consistency
List<PortalRepresentation> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dimIdentifier);
LOGGER.debug("got containing PortalCandidates , {}", portalCandidates);
for (PortalRepresentation portalCandidate : portalCandidates) {
if (!PortalHelper.isValidCandidate(world, portalCandidate.location(), blockId)) {
LOGGER.debug("invalid, {}", portalCandidate);
portalCandidateList.removePortal(blockId, portalCandidate);
}
}
//find new portalCandidates created by PortalBlock placement
@ -51,28 +60,26 @@ public class PortalManager {
LOGGER.debug("new portalCandidates found, {}", portals);
for (BlockBox portal : portals) {
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal, world.getDimensionKey().getValue());
portalCandidateList.createPortal(blockId, portalWithDim);
PortalRepresentation portalRepresentationWithDim = new PortalRepresentation(portal, dimIdentifier);
portalCandidateList.createPortal(blockId, portalRepresentationWithDim);
}
world.getProfiler().pop();
}
public static void onPortalBlockBroken(ServerWorld world, BlockPos pos, Identifier blockId) {
public void onPortalBlockBroken(ServerWorld world, BlockPos pos, Identifier blockId) {
world.getProfiler().push("onPortalBlockBroken");
LOGGER.debug("onPortalBlockBroken, {}", blockId);
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
//check portalCandidate validity
List<BlockBox> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, world.getDimensionKey().getValue());
Identifier dimensionIdentifier = getDimId(world);
List<PortalRepresentation> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dimensionIdentifier);
LOGGER.debug("getContainingPortals, {}", portalCandidates);
for (BlockBox portalCandidate : portalCandidates) {
if (!PortalHelper.isValidCandidate(world, portalCandidate, blockId)) {
for (PortalRepresentation portalCandidate : portalCandidates) {
if (!PortalHelper.isValidCandidate(world, portalCandidate.location(), blockId)) {
LOGGER.debug("invalid, {}", portalCandidate);
Pair<BlockBox, Identifier> portalWithDim =
Pair.of(portalCandidate, world.getDimensionKey().getValue());
portalCandidateList.removePortal(blockId, portalWithDim);
portalCandidateList.removePortal(blockId, portalCandidate);
}
}
@ -84,25 +91,23 @@ public class PortalManager {
LOGGER.debug("potentially new candidate after deobstruction , {}", newPortal);
PortalListComponent portalListComponent = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
Pair<BlockBox, Identifier> portalWithDim =
Pair.of(newPortal, world.getDimensionKey().getValue());
portalListComponent.createPortal(blockId, portalWithDim);
PortalRepresentation portalRepresentationWithDim =
new PortalRepresentation(newPortal, dimensionIdentifier);
portalListComponent.createPortal(blockId, portalRepresentationWithDim);
}
}
//check portal validity
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, world.getDimensionKey().getValue());
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dimensionIdentifier);
boolean changed = false;
for (BlockBox portal : portals) {
if (!PortalHelper.isValidPortal(world, portal, blockId)) {
LOGGER.debug("portal became invalid ,{} {}", world.getDimensionKey().getValue(), portal);
for (PortalRepresentation portal : portals) {
if (!PortalHelper.isValidPortal(world, portal.location(), blockId)) {
LOGGER.debug("portal became invalid ,{} {}", dimensionIdentifier, portal);
Pair<BlockBox, Identifier> portalWithDim =
Pair.of(portal, world.getDimensionKey().getValue());
portalList.removePortal(blockId, portalWithDim);
destroyPortalEntitiesInside(world, Box.from(portal));
removePortalBlocks(world, portal);
portalList.removePortal(blockId, portal);
linkingSystem.unLinkPortal(portal);
removePortalBlocks(world, portal.location());
changed = true;
}
@ -114,55 +119,53 @@ public class PortalManager {
world.getProfiler().pop();
}
private static void removePortalBlocks(ServerWorld world, BlockBox portal) {
private void removePortalBlocks(ServerWorld world, BlockBox portal) {
BlockPos.stream(portal)
.filter(blockPos -> world.getBlockState(blockPos).isOf(PORTAL_BLOCK))
.forEach(blockPos -> world.setBlockState(blockPos, Blocks.AIR.getDefaultState()));
}
private static void fixPortalGroup(Identifier blockId, PortalListComponent portalList, MinecraftServer server) {
List<Pair<BlockBox, Identifier>> portalsWithDim = portalList.getPortals(blockId);
private void fixPortalGroup(Identifier blockId, PortalListComponent portalList, MinecraftServer server) {
LinkedList<PortalRepresentation> portalsWithDim = portalList.getPortals(blockId);
if (portalsWithDim.size() == 1) {
Pair<BlockBox, Identifier> portal = portalsWithDim.get(0);
ServerWorld world = getPortalWorld(portal, server);
PortalRepresentation portalRepresentation = portalsWithDim.getFirst();
ServerWorld world = getPortalWorld(portalRepresentation, server);
if (world == null) {
LOGGER.error("error fixing portalGroup, world {} was null", portal.second);
LOGGER.error("error fixing portalGroup, dimensionId {} was null", portalRepresentation.dimensionId());
return;
}
Pair<BlockBox, Identifier> portalWithDim = Pair.of(portal.first, portal.second);
portalList.removePortal(blockId, portalWithDim);
destroyPortalEntitiesInside(world, Box.from(portal.first));
removePortalBlocks(world, portal.first);
portalList.removePortal(blockId, portalRepresentation);
linkingSystem.unLinkPortal(portalRepresentation);
removePortalBlocks(world, portalRepresentation.location());
return;
}
for (int i = 0; i < portalsWithDim.size(); i++) {
Pair<BlockBox, Identifier> portal = portalsWithDim.get(i);
Pair<BlockBox, Identifier> linkedToPortal = i + 1 < portalsWithDim.size() ? portalsWithDim.get(i + 1) : portalsWithDim.get(0);
assureLinkedTo(portal, linkedToPortal, server);
assurePortalBlocksPlaced(blockId, portal, server);
for (PortalRepresentation portalRepresentation : portalsWithDim) {
assurePortalBlocksPlaced(blockId, portalRepresentation, server);
}
linkingSystem.linkPortals(portalsWithDim);
}
private static void assurePortalBlocksPlaced(Identifier blockId, Pair<BlockBox, Identifier> portal, MinecraftServer server) {
DyeColor color = ColourfulPortalConfig.portalBlocks.get(blockId.toString());
private void assurePortalBlocksPlaced(Identifier blockId, PortalRepresentation portalRepresentation, MinecraftServer server) {
DyeColor color = ColourfulPortalConfig.colorOf(blockId.toString());
if (color != null) {
Direction.Axis portalPlaneAxis = PortalHelper.getAxisW(portal.first)
Direction.Axis portalPlaneAxis = PortalHelper.getAxisW(portalRepresentation.location())
.rotateClockwise(Direction.Axis.Y)
.getAxis();
ServerWorld portalWorld = getPortalWorld(portal, server);
ServerWorld portalWorld = getPortalWorld(portalRepresentation, server);
if (portalWorld == null) {
LOGGER.error("error placing portal planes, world {} was null", portal.second);
LOGGER.error("error placing portalRepresentation planes, dimensionId {} was null", portalRepresentation.dimensionId());
return;
}
BlockState portalBlockState = PORTAL_BLOCK.getStateWith(color, portalPlaneAxis);
PortalHelper.insideOf(portal.first)
PortalHelper.insideOf(portalRepresentation.location())
.forEachRemaining(blockPos -> {
BlockState blockState = portalWorld.getBlockState(blockPos);
if (blockState.isAir() || blockState.getFluidState().isOf(PORTAL_FLUID)) {
portalWorld.setBlockState(blockPos, portalBlockState);
} else if (blockState.isReplaceable()) {
portalWorld.breakBlock(blockPos,true);
portalWorld.breakBlock(blockPos, true);
portalWorld.setBlockState(blockPos, portalBlockState);
}
});
@ -170,99 +173,27 @@ public class PortalManager {
}
private static void assureLinkedTo(Pair<BlockBox, Identifier> fromPortal, Pair<BlockBox, Identifier> linkedToPortal, MinecraftServer server) {
ServerWorld fromPortalWorld = getPortalWorld(fromPortal, server);
RegistryKey<World> linkedToPortalWorldRegKey = RegistryKey.of(RegistryKeys.WORLD, linkedToPortal.second);
BlockBox fromPortalBlockBox = fromPortal.first;
Box fromPortalBox = Box.from(fromPortalBlockBox);
BlockBox linkedToPortalBlockBox = linkedToPortal.first;
Box linkedToPortalBox = Box.from(linkedToPortalBlockBox);
if (fromPortalWorld == null) {
LOGGER.error("error linking portals, world {} was null", fromPortalBlockBox);
return;
}
List<Portal> portals = fromPortalWorld.getEntitiesByType(TypeFilter.instanceOf(Portal.class), fromPortalBox, Entity::isAlive);
List<Portal> outgoingPortals = new ArrayList<>();
for (Portal portal : portals) {
Box portalBoundingBox = portal.getBoundingBox();
//is portal actually inside portalBox?
if (fromPortalBox.contains(portalBoundingBox.minX, portalBoundingBox.minY, portalBoundingBox.minZ)
&& fromPortalBox.contains(portalBoundingBox.maxX, portalBoundingBox.maxY, portalBoundingBox.maxZ)) {
outgoingPortals.add(portal);
}
}
if (outgoingPortals.size() > 2) {
LOGGER.warn("Found more then 2 portals in {}, cleaning up", fromPortalBox);
for (int i = 2; i < outgoingPortals.size(); i++) {
Portal outgoingPortal = outgoingPortals.get(i);
outgoingPortal.kill();
}
}
if (outgoingPortals.isEmpty()) {
Portal portal = Portal.ENTITY_TYPE.create(fromPortalWorld);
if (portal == null) {
LOGGER.error("could not create Portal entity for {}", fromPortal);
return;
}
portal.setOriginPos(fromPortalBox.getCenter());
portal.setDestinationDimension(linkedToPortalWorldRegKey);
portal.setDestination(linkedToPortalBox.getCenter());
Vec3d axisW = Vec3d.of(PortalHelper.getAxisW(fromPortalBlockBox).getVector());
portal.setOrientationAndSize(
axisW, // axisW
new Vec3d(0, 1, 0), // axisH
2, // width
3 // height
);
portal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
outgoingPortals.add(portal);
PortalAPI.spawnServerEntity(portal);
}
if (outgoingPortals.size() == 1) {
Portal portal = PortalAPI.createFlippedPortal(outgoingPortals.get(0));
outgoingPortals.add(portal);
PortalAPI.spawnServerEntity(portal);
}
for (Portal outgoingPortal : outgoingPortals) {
outgoingPortal.setDestinationDimension(linkedToPortalWorldRegKey);
outgoingPortal.setDestination(linkedToPortalBox.getCenter());
Vec3d axisW = Vec3d.of(PortalHelper.getAxisW(fromPortalBlockBox).getVector());
outgoingPortal.setRotationTransformation(DQuaternion.getRotationBetween(axisW, Vec3d.of(PortalHelper.getAxisW(linkedToPortalBlockBox).getVector())));
outgoingPortal.reloadAndSyncToClient();
}
}
@Nullable
private static ServerWorld getPortalWorld(Pair<BlockBox, Identifier> fromPortal, MinecraftServer server) {
return server.getWorld(RegistryKey.of(RegistryKeys.WORLD, fromPortal.second));
private ServerWorld getPortalWorld(PortalRepresentation fromPortalRepresentation, MinecraftServer server) {
return server.getWorld(RegistryKey.of(RegistryKeys.WORLD, fromPortalRepresentation.dimensionId()));
}
private static void destroyPortalEntitiesInside(ServerWorld world, Box box) {
List<Portal> portals = world.getEntitiesByType(TypeFilter.instanceOf(Portal.class), box, Entity::isAlive);
portals.forEach(portal -> PortalAPI.removeGlobalPortal(world, portal));
}
public static boolean tryIgnite(ServerWorld world, BlockPos pos) {
public boolean tryIgnite(ServerWorld world, BlockPos pos) {
world.getProfiler().push("portal ignition");
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
LOGGER.info("portal ignition");
Set<Identifier> blockIds = portalCandidateList.getBlockIds();
boolean ret = false;
for (Identifier blockId : blockIds) {
Identifier dim = world.getDimensionKey().getValue();
List<BlockBox> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dim);
for (BlockBox portalCandidate : portalCandidates) {
if (PortalHelper.isValidPortal(world, portalCandidate, blockId)) {
Pair<BlockBox, Identifier> current = Pair.of(portalCandidate, dim);
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, portalCandidate, dim);
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
Identifier dim = getDimId(world);
List<PortalRepresentation> portalCandidates = portalCandidateList.getContainingPortals(blockId, pos, dim);
for (PortalRepresentation current : portalCandidates) {
if (PortalHelper.isValidPortal(world, current.location(), blockId)) {
PortalRepresentation next = portalCandidateList.getNext(blockId, current);
ServerWorld nextWorld = getPortalWorld(next, world.getServer());
if (!next.equals(current)
&& (!portalList.containsPortal(blockId, current) || !portalList.containsPortal(blockId, next))
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)) {
&& PortalHelper.isPortalPlaceable(nextWorld, next.location(), blockId)) {
portalList.createPortal(blockId, current);
portalList.createPortal(blockId, next);
ret = true;
@ -275,24 +206,21 @@ public class PortalManager {
return ret;
}
public static boolean canExtend(ServerWorld world, BlockPos pos) {
public boolean canExtend(ServerWorld world, BlockPos pos) {
world.getProfiler().push("portal extension check");
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
Set<Identifier> blockIds = portalList.getBlockIds();
Identifier dim = world.getDimensionKey().getValue();
Identifier dim = getDimId(world);
for (Identifier blockId : blockIds) {
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, dim);
for (BlockBox portal : portals) {
if (PortalHelper.isValidPortal(world, portal, blockId)) {
Pair<BlockBox, Identifier> last = portalList.getLast(blockId);
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, last.first, last.second);
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dim);
for (PortalRepresentation portal : portals) {
if (PortalHelper.isValidPortal(world, portal.location(), blockId)) {
PortalRepresentation last = portalList.getLast(blockId);
PortalRepresentation next = portalCandidateList.getNext(blockId, last);
ServerWorld nextWorld = getPortalWorld(next, world.getServer());
if(!portalList.containsPortal(blockId, next)
&& PortalHelper.isValidPortal(world, portal, blockId)
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)){
if (!portalList.containsPortal(blockId, next)
&& PortalHelper.isPortalPlaceable(nextWorld, next.location(), blockId)) {
world.getProfiler().pop();
return true;
}
@ -305,25 +233,22 @@ public class PortalManager {
return false;
}
public static boolean extend(ServerWorld world, BlockPos pos) {
public boolean extend(ServerWorld world, BlockPos pos) {
world.getProfiler().push("portal extension");
PortalListComponent portalCandidateList = PORTAL_CANDIDATE_LIST.get(world.getLevelProperties());
PortalListComponent portalList = PORTAL_LIST.get(world.getLevelProperties());
Set<Identifier> blockIds = portalList.getBlockIds();
Identifier dim = world.getDimensionKey().getValue();
Identifier dim = getDimId(world);
boolean ret = false;
for (Identifier blockId : blockIds) {
List<BlockBox> portals = portalList.getContainingPortals(blockId, pos, dim);
for (BlockBox portal : portals) {
if (PortalHelper.isValidPortal(world, portal, blockId)) {
Pair<BlockBox, Identifier> last = portalList.getLast(blockId);
Pair<BlockBox, Identifier> next = portalCandidateList.getNext(blockId, last.first, last.second);
ServerWorld nextWorld = world.getServer().getWorld(RegistryKey.of(RegistryKeys.WORLD, next.second));
List<PortalRepresentation> portals = portalList.getContainingPortals(blockId, pos, dim);
for (PortalRepresentation portal : portals) {
if (PortalHelper.isValidPortal(world, portal.location(), blockId)) {
PortalRepresentation last = portalList.getLast(blockId);
PortalRepresentation next = portalCandidateList.getNext(blockId, last);
ServerWorld nextWorld = getPortalWorld(next, world.getServer());
if(!portalList.containsPortal(blockId, next)
&& PortalHelper.isValidPortal(world, portal, blockId)
&& PortalHelper.isPortalPlaceable(nextWorld, next.first, blockId)){
if (!portalList.containsPortal(blockId, next)
&& PortalHelper.isPortalPlaceable(nextWorld, next.location(), blockId)) {
portalList.createPortal(blockId, next);
ret = true;
}
@ -336,4 +261,12 @@ public class PortalManager {
world.getProfiler().pop();
return ret;
}
public static Identifier getDimId(ServerWorld world) {
return world.getDimensionEntry().getKey().orElseThrow().getValue();
}
public void onPortalPassed(Entity entity, BlockPos pos, ServerWorld world, Direction.Axis a) {
linkingSystem.onPortalPassed(entity, pos, world, a);
}
}

View File

@ -0,0 +1,9 @@
package quimufu.colourful_portals.portal;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockBox;
public record PortalRepresentation(BlockBox location, Identifier dimensionId) {
}

View File

@ -0,0 +1,4 @@
package quimufu.colourful_portals.portal;
public record PrioritizedPortalLinkingSystemBuilder(PortalLinkingSystemBuilder portalLinkingSystemBuilder,
int priority) {}

View File

@ -31,6 +31,7 @@ import quimufu.colourful_portals.portal.PortalManager;
import java.util.*;
import static quimufu.colourful_portals.ColourfulPortalsMod.LOGGER;
import static quimufu.colourful_portals.ColourfulPortalsMod.PORTAL_MANAGER;
public class PortalFluid extends Fluid {
public static final EnumProperty<NullableAxis> AXIS = EnumProperty.of("axis", NullableAxis.class);
@ -138,7 +139,7 @@ public class PortalFluid extends Fluid {
}
if (amount <= 14) {
//*might* change my state in world, returned value is what new state should be
//*might* change my state in dimensionId, returned value is what new state should be
newStateMe = steal(world, pos, newStateMe, currentAxis);
}
//my state might have changed.
@ -169,7 +170,7 @@ public class PortalFluid extends Fluid {
} else if (!world.isClient) {
for (Direction direction : Direction.values()) {
if (ColourfulPortalsMod.PORTAL_BLOCKS.contains(Registries.BLOCK.getId(world.getBlockState(pos.offset(direction)).getBlock()))
&& PortalManager.tryIgnite((ServerWorld) world, pos)) {
&& PORTAL_MANAGER.tryIgnite((ServerWorld) world, pos)) {
return;
}
}
@ -372,7 +373,7 @@ public class PortalFluid extends Fluid {
BlockPos neighborOfInspectedLocation = inspectedLocation.offset(dir);
BlockState neighbourOfInspected = world.getBlockState(neighborOfInspectedLocation);
//if adjacent to a Portal Block, 2 less
//if adjacent to a PortalRepresentation Block, 2 less
if (ColourfulPortalsMod.PORTAL_BLOCKS.contains(Registries.BLOCK.getId(neighbourOfInspected.getBlock()))) {
dirCost -= 2;
discount = false;
@ -381,7 +382,7 @@ public class PortalFluid extends Fluid {
//if adjacent to any solid Block, 1 less
if (neighbourOfInspected.isSideSolidFullSquare(world, neighborOfInspectedLocation, dir.getOpposite())) {
discount = true;
//maybe a Portal Block in another direction
//maybe a PortalRepresentation Block in another direction
continue;
}
}

View File

@ -108,7 +108,7 @@ public class PortalFluidBlock
}
@Override
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
protected boolean canPathfindThrough(BlockState state, NavigationType type) {
return true;
}

View File

@ -0,0 +1,32 @@
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

@ -1,31 +0,0 @@
package quimufu.colourful_portals.util;
import java.util.Objects;
public class Pair<A, B> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
public static <A, B> Pair<A, B> of(A first, B second) {
return new Pair<>(first, second);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair<?, ?> pair = (Pair<?, ?>) o;
return Objects.equals(first, pair.first) && Objects.equals(second, pair.second);
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
}

View File

@ -1,7 +1,26 @@
{
"item.colourful_portals.portal_fluid_bucket": "Farbeimer",
"block.colourful_portals.portal_block": "Farbenfrohes Portal",
"block.colourful_portals.portal_fluid_block": "Farbenfrohe Flüssigkeit",
"colourful_portals.midnightconfig.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.gray": "Blöcke, die graue Portale erzeugen",
"colourful_portals.midnightconfig.green": "Blöcke, die grüne Portale erstellen",
"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.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",
"item.colourful_portals.colour_blob_bright": "Heller Farbmix",
"item.colourful_portals.colour_blob_dark": "Dunkler Farbmix",
"block.colourful_portals.portal_fluid_block": "Farbenfrohe Flüssigkeit"
"item.colourful_portals.portal_fluid_bucket": "Farbeimer"
}

View File

@ -1,7 +1,26 @@
{
"item.colourful_portals.portal_fluid_bucket": "Colourful Bucket",
"block.colourful_portals.portal_block": "Colourful Portal",
"block.colourful_portals.portal_fluid_block": "Colourful Fluid",
"colourful_portals.midnightconfig.disableImmersivePortals": "Disable Immersive Portals integration",
"colourful_portals.midnightconfig.black": "Blocks that create black portals",
"colourful_portals.midnightconfig.blue": "Blocks that create blue portals",
"colourful_portals.midnightconfig.brown": "Blocks that create brown portals",
"colourful_portals.midnightconfig.cyan": "Blocks that create cyan portals",
"colourful_portals.midnightconfig.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.gray": "Blocks that create gray portals",
"colourful_portals.midnightconfig.green": "Blocks that create green portals",
"colourful_portals.midnightconfig.light_blue": "Blocks that create light blue portals",
"colourful_portals.midnightconfig.light_gray": "Blocks that create light gray portals",
"colourful_portals.midnightconfig.lime": "Blocks that create lime portals",
"colourful_portals.midnightconfig.magenta": "Blocks that create magenta portals",
"colourful_portals.midnightconfig.none": "Blocks that create fully transparent portals",
"colourful_portals.midnightconfig.orange": "Blocks that create orange portals",
"colourful_portals.midnightconfig.pink": "Blocks that create pink portals",
"colourful_portals.midnightconfig.purple": "Blocks that create purple portals",
"colourful_portals.midnightconfig.red": "Blocks that create red portals",
"colourful_portals.midnightconfig.white": "Blocks that create white portals",
"colourful_portals.midnightconfig.yellow": "Blocks that create yellow portals",
"item.colourful_portals.colour_blob_bright": "Bright Colour Mix",
"item.colourful_portals.colour_blob_dark": "Dark Colour Mix",
"block.colourful_portals.portal_block": "Colourful Portal",
"block.colourful_portals.portal_fluid_block": "Colourful Fluid"
"item.colourful_portals.portal_fluid_bucket": "Colourful Bucket"
}

View File

@ -3,17 +3,18 @@
"package": "quimufu.colourful_portals.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"BlockChangeMixin"
"BlockChangeMixin",
"EntityMixin"
],
"injectors": {
"defaultRequire": 1
},
"plugin": "quimufu.colourful_portals.MixinConfig",
"client": [
"AnimationResourceMetadataReaderMixin",
"AnimationMixin",
"AnimationResourceMetadataMixin",
"SpriteContentsMixin",
"SodiumFluidRendererMixin"
"AnimationResourceMetadataReaderMixin",
"SodiumFluidRendererMixin",
"SpriteContentsMixin"
]
}

View File

@ -42,9 +42,8 @@
],
"depends": {
"fabricloader": ">=${loader_version}",
"immersive_portals": ">=${immersive_portals_version_short}",
"minecraft": "~${minecraft_version}",
"java": ">=17",
"java": ">=21",
"fabric-api": "*"
}
}