mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-11 22:45:38 -04:00
Rework Dimensions for 1.16 (#627)
* dim api initial port * A whole bunch of work * Checkstyle :) * Minor tweaks based on feedback * Update to latest snapshot * Checkstyle ;) * Some more dim work * Re add default placer's, the example mod includes a test dim that currently marks the world as experimental * license * Fixup javadoc
This commit is contained in:
parent
5a6e8f4ce1
commit
a71b3053ad
24 changed files with 257 additions and 780 deletions
fabric-dimensions-v1
build.gradle
settings.gradlesrc
main
java/net/fabricmc/fabric
api/dimension/v1
impl/dimension
DimensionIdsFixer.javaDimensionIdsHolder.javaDimensionRemapException.javaFabricDimensionClientInit.javaFabricDimensionInternals.java
mixin/dimension
resources
testmod
|
@ -1,8 +1,10 @@
|
|||
archivesBaseName = "fabric-dimensions-v1"
|
||||
version = getSubprojectVersion(project, "0.4.2")
|
||||
version = getSubprojectVersion(project, "1.0.0")
|
||||
|
||||
dependencies {
|
||||
compile project(path: ':fabric-api-base', configuration: 'dev')
|
||||
compile project(path: ':fabric-networking-v0', configuration: 'dev')
|
||||
compile project(path: ':fabric-registry-sync-v0', configuration: 'dev')
|
||||
|
||||
testmodCompile project(path: ':fabric-command-api-v1', configuration: 'dev')
|
||||
}
|
||||
|
|
|
@ -23,16 +23,18 @@ import net.minecraft.util.math.Direction;
|
|||
|
||||
/**
|
||||
* Responsible for placing an Entity once they have entered a dimension.
|
||||
* Stored by a FabricDimensionType, and used in Entity::changeDimension.
|
||||
* used in Entity::changeDimension.
|
||||
*
|
||||
* @deprecated Experimental feature, may be removed or changed without further notice due to potential changes to Dimensions in subsequent versions.
|
||||
*
|
||||
* @see FabricDimensions
|
||||
* @see FabricDimensionType
|
||||
*/
|
||||
@Deprecated
|
||||
@FunctionalInterface
|
||||
public interface EntityPlacer {
|
||||
/**
|
||||
* Handles the placement of an entity going to a dimension.
|
||||
* Utilized by {@link FabricDimensions#teleport(Entity, net.minecraft.world.dimension.DimensionType, EntityPlacer)} to specify placement logic when needed.
|
||||
* Utilized by {@link FabricDimensions#teleport(Entity, ServerWorld, EntityPlacer)} to specify placement logic when needed.
|
||||
*
|
||||
* <p>This method may have side effects such as the creation of a portal in the target dimension,
|
||||
* or the creation of a chunk loading ticket.
|
||||
|
|
|
@ -1,244 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.dimension.v1;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.source.BiomeAccessType;
|
||||
import net.minecraft.world.biome.source.VoronoiBiomeAccessType;
|
||||
import net.minecraft.world.dimension.Dimension;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
/**
|
||||
* An extended version of {@link DimensionType} with automatic raw id management and default placement settings.
|
||||
* {@code FabricDimensionType} instances are constructed and registered through a {@link Builder}.
|
||||
*
|
||||
* @see #builder()
|
||||
* @see #getDefaultPlacement()
|
||||
* @see #getDesiredRawId()
|
||||
*/
|
||||
public final class FabricDimensionType extends DimensionType {
|
||||
private final EntityPlacer defaultPlacement;
|
||||
private int desiredRawId;
|
||||
/** The fixed raw id for this dimension type, set through reflection. */
|
||||
private int fixedRawId;
|
||||
|
||||
/**
|
||||
* Returns a new {@link Builder}.
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new FabricDimensionType.Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param suffix the string suffix unique to the dimension type
|
||||
* @param saveDir the name of the save directory for the dimension type
|
||||
* @param builder builder instance containing other parameters
|
||||
* @see #builder()
|
||||
*/
|
||||
private FabricDimensionType(String suffix, String saveDir, Builder builder) {
|
||||
// Pass an arbitrary raw id that does not map to any vanilla dimension. That id should never get used.
|
||||
super(3, suffix, saveDir, builder.factory, builder.skyLight, false, false, builder.biomeAccessStrategy);
|
||||
this.defaultPlacement = builder.defaultPlacer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the desired raw id of this dimension type.
|
||||
*
|
||||
* @return the preferred raw id of this dimension type
|
||||
* @see Builder#desiredRawId(int)
|
||||
*/
|
||||
public int getDesiredRawId() {
|
||||
return desiredRawId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current raw id for this dimension type.
|
||||
*
|
||||
* <p>The returned id is guaranteed to be unique and persistent in a save,
|
||||
* as well as synchronized between a server and its connected clients.
|
||||
* It may change when connecting to a different server or opening a new save.
|
||||
*
|
||||
* @return the current raw id for this dimension type
|
||||
* @see #getDesiredRawId()
|
||||
*/
|
||||
@Override
|
||||
public int getRawId() {
|
||||
return this.fixedRawId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default placement logic for this dimension. The returned placer
|
||||
* never returns {@code null} when called.
|
||||
*
|
||||
* @return the default placement logic for this dimension
|
||||
* @see FabricDimensions#teleport(Entity, DimensionType, EntityPlacer)
|
||||
*/
|
||||
public EntityPlacer getDefaultPlacement() {
|
||||
return this.defaultPlacement;
|
||||
}
|
||||
|
||||
/**
|
||||
* A builder for creating and registering {@code FabricDimensionType} instances. Example: <pre> {@code
|
||||
*
|
||||
* public static final FabricDimensionType MY_DIMENSION
|
||||
* = FabricDimensionType.builder()
|
||||
* .defaultPlacement((oldEntity, destination, portalDir, horizontalOffset, verticalOffset) ->
|
||||
* new BlockPattern.TeleportTarget(new Vec3d(0, 100, 0), teleported.getVelocity(), 0))
|
||||
* .factory(MyDimension::new)
|
||||
* .skyLight(true)
|
||||
* .buildAndRegister();}</pre>
|
||||
*
|
||||
* <p>Builder instances can be reused; it is safe to call {@link #buildAndRegister(Identifier)} multiple
|
||||
* times (with different identifiers) to build and register multiple dimension types in series.
|
||||
* Each new dimension type uses the settings of the builder at the time it is built.
|
||||
*
|
||||
* @see FabricDimensionType#builder()
|
||||
*/
|
||||
public static final class Builder {
|
||||
private EntityPlacer defaultPlacer;
|
||||
private BiFunction<World, DimensionType, ? extends Dimension> factory;
|
||||
private int desiredRawId = 0;
|
||||
private boolean skyLight = true;
|
||||
private BiomeAccessType biomeAccessStrategy = VoronoiBiomeAccessType.INSTANCE;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default placer used when teleporting entities to dimensions of the built type.
|
||||
* The default placer must be set before building a dimension type.
|
||||
*
|
||||
* <p>A dimension type's default placer must never return {@code null} when its
|
||||
* {@link EntityPlacer#placeEntity(Entity, ServerWorld, Direction, double, double) placeEntity} method
|
||||
* is called.
|
||||
*
|
||||
* @param defaultPlacer a default entity placer for dimensions of the built type
|
||||
* @return this {@code Builder} object
|
||||
* @throws NullPointerException if {@code defaultPlacer} is {@code null}
|
||||
*/
|
||||
public Builder defaultPlacer(EntityPlacer defaultPlacer) {
|
||||
Preconditions.checkNotNull(defaultPlacer);
|
||||
|
||||
this.defaultPlacer = defaultPlacer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the factory used to create new {@link Dimension} instances of the built type.
|
||||
* The dimension factory must be set before building a dimension type.
|
||||
*
|
||||
* @param factory a function creating new {@code Dimension} instances
|
||||
* @return this {@code Builder} object
|
||||
* @throws NullPointerException if {@code factory} is {@code null}
|
||||
*/
|
||||
public Builder factory(BiFunction<World, DimensionType, ? extends Dimension> factory) {
|
||||
Preconditions.checkNotNull(factory);
|
||||
|
||||
this.factory = factory;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether built dimension types use skylight like the Overworld.
|
||||
* If this method is not called, the value defaults to {@code true}.
|
||||
*
|
||||
* @param skyLight {@code true} if the dimension of the built type should use skylight,
|
||||
* {@code false} otherwise
|
||||
* @return this {@code Builder} object
|
||||
*/
|
||||
public Builder skyLight(boolean skyLight) {
|
||||
this.skyLight = skyLight;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Governs how biome information is retrieved from random seed and world coordinates.
|
||||
* If this method is not called, value defaults to the three-dimensional strategy
|
||||
* used by the End and Nether dimensions.
|
||||
*
|
||||
* @param biomeAccessStrategy Function to be used for biome generation.
|
||||
* @return this {@code Builder} object
|
||||
*/
|
||||
public Builder biomeAccessStrategy(BiomeAccessType biomeAccessStrategy) {
|
||||
Preconditions.checkNotNull(biomeAccessStrategy);
|
||||
|
||||
this.biomeAccessStrategy = biomeAccessStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this dimension's desired raw id.
|
||||
* If this method is not called, the value defaults to the raw registry id
|
||||
* of the dimension type.
|
||||
*
|
||||
* <p>A Fabric Dimension's desired raw id is used as its actual raw id
|
||||
* when it does not conflict with any existing id, and the world
|
||||
* save does not map the dimension to a different raw id.
|
||||
*
|
||||
* @param desiredRawId the new raw id for this dimension type
|
||||
* @return this {@code Builder} object
|
||||
* @apiNote Mods that used to have a dimension with a manually set id
|
||||
* may use this method to set a default id corresponding to the old one,
|
||||
* so as not to break compatibility with old worlds.
|
||||
*/
|
||||
public Builder desiredRawId(int desiredRawId) {
|
||||
this.desiredRawId = desiredRawId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and register a {@code FabricDimensionType}.
|
||||
*
|
||||
* <p>The {@code dimensionId} is used as a registry ID, and as
|
||||
* a unique name both for the dimension suffix and the save directory.
|
||||
*
|
||||
* @param dimensionId the id used to name and register the dimension
|
||||
* @return the built {@code FabricDimensionType}
|
||||
* @throws IllegalArgumentException if an existing dimension has already been registered with {@code dimensionId}
|
||||
* @throws IllegalStateException if no {@link #factory(BiFunction) factory} or {@link #defaultPlacer(EntityPlacer) default placer}
|
||||
* have been set
|
||||
*/
|
||||
public FabricDimensionType buildAndRegister(Identifier dimensionId) {
|
||||
Preconditions.checkArgument(Registry.DIMENSION_TYPE.get(dimensionId) == null);
|
||||
Preconditions.checkState(this.defaultPlacer != null, "No defaultPlacer has been specified!");
|
||||
Preconditions.checkState(this.factory != null, "No dimension factory has been specified!");
|
||||
|
||||
String suffix = dimensionId.getNamespace() + "_" + dimensionId.getPath();
|
||||
String saveDir = "DIM_" + dimensionId.getNamespace() + "_" + dimensionId.getPath();
|
||||
FabricDimensionType built = new FabricDimensionType(suffix, saveDir, this);
|
||||
Registry.register(Registry.DIMENSION_TYPE, dimensionId, built);
|
||||
|
||||
if (this.desiredRawId != 0) {
|
||||
built.desiredRawId = this.desiredRawId;
|
||||
} else {
|
||||
built.desiredRawId = Registry.DIMENSION_TYPE.getRawId(built) - 1;
|
||||
}
|
||||
|
||||
built.fixedRawId = built.desiredRawId;
|
||||
return built;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,13 +19,18 @@ package net.fabricmc.fabric.api.dimension.v1;
|
|||
import com.google.common.base.Preconditions;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import net.fabricmc.fabric.impl.dimension.FabricDimensionInternals;
|
||||
|
||||
/**
|
||||
* This class consists exclusively of static methods that operate on world dimensions.
|
||||
*
|
||||
* @deprecated Experimental feature, may be removed or changed without further notice due to potential changes to Dimensions in subsequent versions.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class FabricDimensions {
|
||||
private FabricDimensions() {
|
||||
throw new AssertionError();
|
||||
|
@ -37,8 +42,7 @@ public final class FabricDimensions {
|
|||
* <p>This method behaves as if:
|
||||
* <pre>{@code teleported.changeDimension(destination)}</pre>
|
||||
*
|
||||
* <p>If {@code destination} is a {@link FabricDimensionType}, the placement logic used
|
||||
* is {@link FabricDimensionType#getDefaultPlacement()}. If {@code destination} is
|
||||
* <p>If {@code destination} has a default placer, that placer will be used. If {@code destination} is
|
||||
* the nether or the overworld, the default logic is the vanilla path.
|
||||
* For any other dimension, the default placement behaviour is undefined.
|
||||
* When delegating to a placement logic that uses portals, the entity's {@code lastPortalPosition},
|
||||
|
@ -51,9 +55,9 @@ public final class FabricDimensions {
|
|||
* @param teleported the entity to teleport
|
||||
* @param destination the dimension the entity will be teleported to
|
||||
* @return the teleported entity, or a clone of it
|
||||
* @see #teleport(Entity, DimensionType, EntityPlacer)
|
||||
* @see #teleport(Entity, ServerWorld)
|
||||
*/
|
||||
public static <E extends Entity> E teleport(E teleported, DimensionType destination) {
|
||||
public static <E extends Entity> E teleport(E teleported, ServerWorld destination) {
|
||||
return teleport(teleported, destination, null);
|
||||
}
|
||||
|
||||
|
@ -65,9 +69,8 @@ public final class FabricDimensions {
|
|||
* The {@code customPlacement} may itself return {@code null}, in which case
|
||||
* the default placement logic for that dimension will be run.
|
||||
*
|
||||
* <p>If {@code destination} is a {@link FabricDimensionType}, the default placement logic
|
||||
* is {@link FabricDimensionType#getDefaultPlacement()}. If {@code destination} is the nether
|
||||
* or the overworld, the default logic is the vanilla path.
|
||||
* <p>If {@code destination} has a default placer, that placer will be used. If {@code destination} is
|
||||
* the nether or the overworld, the default logic is the vanilla path.
|
||||
* For any other dimension, the default placement behaviour is undefined.
|
||||
* When delegating to a placement logic that uses portals, the entity's {@code lastPortalPosition},
|
||||
* {@code lastPortalDirectionVector}, and {@code lastPortalDirection} fields should be updated
|
||||
|
@ -79,15 +82,29 @@ public final class FabricDimensions {
|
|||
* @param teleported the entity to teleport
|
||||
* @param destination the dimension the entity will be teleported to
|
||||
* @param customPlacer custom placement logic that will run before the default one,
|
||||
* or {@code null} to use the dimension's default behavior (see {@link FabricDimensionType#getDefaultPlacement()}).
|
||||
* or {@code null} to use the dimension's default behavior.
|
||||
* @param <E> the type of the teleported entity
|
||||
* @return the teleported entity, or a clone of it
|
||||
* @throws IllegalStateException if this method is called on a client entity
|
||||
* @apiNote this method must be called from the main server thread
|
||||
*/
|
||||
public static <E extends Entity> E teleport(E teleported, DimensionType destination, /*Nullable*/ EntityPlacer customPlacer) {
|
||||
public static <E extends Entity> E teleport(E teleported, ServerWorld destination, /*Nullable*/ EntityPlacer customPlacer) {
|
||||
Preconditions.checkState(!teleported.world.isClient, "Entities can only be teleported on the server side");
|
||||
|
||||
return FabricDimensionInternals.changeDimension(teleported, destination, customPlacer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a default placer for a dimension, this is used when an entity is teleported to a dimension without
|
||||
* a specified {@link EntityPlacer}.
|
||||
*
|
||||
* @param registryKey The dimension {@link RegistryKey}
|
||||
* @param entityPlacer The {@link EntityPlacer}
|
||||
*/
|
||||
public static void registerDefaultPlacer(RegistryKey<World> registryKey, EntityPlacer entityPlacer) {
|
||||
Preconditions.checkState(!FabricDimensionInternals.DEFAULT_PLACERS.containsKey(registryKey), "Only 1 EntityPlacer can be registered per dimension");
|
||||
Preconditions.checkState(!registryKey.getValue().getNamespace().equals("minecraft"), "Minecraft dimensions cannot have a default placer");
|
||||
|
||||
FabricDimensionInternals.DEFAULT_PLACERS.put(registryKey, entityPlacer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.dimension;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
import net.fabricmc.fabric.api.dimension.v1.FabricDimensionType;
|
||||
import net.fabricmc.fabric.api.network.ServerSidePacketRegistry;
|
||||
import net.fabricmc.fabric.impl.registry.sync.RemapException;
|
||||
|
||||
/**
|
||||
* Handles fixing raw dimension ids between saves and servers,
|
||||
* and synchronizes said ids.
|
||||
*/
|
||||
public class DimensionIdsFixer {
|
||||
private static final Field FABRIC_DIMENSION_TYPE$RAW_ID;
|
||||
static final Identifier ID = new Identifier("fabric", "dimension/sync");
|
||||
|
||||
/**
|
||||
* Assigns a unique id to every registered {@link FabricDimensionType}, keeping the known ids
|
||||
* from {@code savedIds}.
|
||||
*
|
||||
* @param savedIds a compound tag mapping dimension ids to raw ids
|
||||
* @return id to raw id mappings of the current instance
|
||||
* @throws RemapException if dimensions IDs conflict irredeemably
|
||||
*/
|
||||
public static CompoundTag apply(CompoundTag savedIds) throws RemapException {
|
||||
/*
|
||||
* We want to give to each fabric dimension a unique ID. We also want to give back previously assigned ids.
|
||||
* And we have to take into account non-fabric dimensions, which raw IDs cannot change.
|
||||
* So we iterate over every dimension, note the ones which id cannot change, then update the free ones.
|
||||
*/
|
||||
Int2ObjectMap<Identifier> fixedIds = new Int2ObjectOpenHashMap<>();
|
||||
List<FabricDimensionType> fabricDimensions = new ArrayList<>();
|
||||
CompoundTag fabricDimensionIds = new CompoundTag();
|
||||
|
||||
// step 1: detect all fabric and non-fabric dimensions
|
||||
for (Identifier id : Registry.DIMENSION_TYPE.getIds()) {
|
||||
DimensionType dimensionType = Objects.requireNonNull(DimensionType.byId(id));
|
||||
|
||||
if (dimensionType instanceof FabricDimensionType) {
|
||||
FabricDimensionType fabricDimension = (FabricDimensionType) dimensionType;
|
||||
fabricDimensions.add(fabricDimension);
|
||||
// reset the fixed raw id to the preferred raw id
|
||||
setFixedRawId(fabricDimension, fabricDimension.getDesiredRawId());
|
||||
} else {
|
||||
Identifier existing = fixedIds.put(dimensionType.getRawId(), id);
|
||||
|
||||
if (existing != null) {
|
||||
throw new RemapException("Two non-fabric dimensions have the same raw dim id (" + dimensionType.getRawId() + ") : " + existing + " and " + id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 2: read saved ids
|
||||
for (String key : savedIds.getKeys()) {
|
||||
int savedRawId = savedIds.getInt(key);
|
||||
Identifier dimId = new Identifier(key);
|
||||
Identifier existing = fixedIds.putIfAbsent(savedRawId, dimId);
|
||||
|
||||
if (existing != null && !existing.equals(dimId)) {
|
||||
throw new RemapException("Saved fabric dimension got replaced with a non-fabric one! " + dimId + " replaced with " + existing + " (raw id: " + savedRawId + ")");
|
||||
}
|
||||
|
||||
DimensionType dim = DimensionType.byId(dimId);
|
||||
|
||||
if (dim instanceof FabricDimensionType) {
|
||||
setFixedRawId((FabricDimensionType) dim, savedRawId);
|
||||
} else {
|
||||
FabricDimensionInternals.LOGGER.warn("A saved dimension has {}: {}", dim == null ? "been removed" : "stopped using the dimensions API", dimId);
|
||||
// Preserve saved ids in case the mod is eventually added back
|
||||
fabricDimensionIds.putInt(dimId.toString(), savedRawId);
|
||||
}
|
||||
}
|
||||
|
||||
// step 3: de-duplicate raw ids for dimensions which ids are not fixed yet
|
||||
int nextFreeId = 0;
|
||||
|
||||
for (FabricDimensionType fabricDimension : fabricDimensions) {
|
||||
int rawDimId = fabricDimension.getRawId();
|
||||
Identifier dimId = Objects.requireNonNull(DimensionType.getId(fabricDimension));
|
||||
|
||||
if (fixedIds.containsKey(rawDimId) && !fixedIds.get(rawDimId).equals(dimId)) {
|
||||
while (fixedIds.containsKey(nextFreeId)) ++nextFreeId;
|
||||
setFixedRawId(fabricDimension, nextFreeId);
|
||||
rawDimId = nextFreeId;
|
||||
}
|
||||
|
||||
fixedIds.put(rawDimId, dimId);
|
||||
fabricDimensionIds.putInt(dimId.toString(), rawDimId);
|
||||
}
|
||||
|
||||
return fabricDimensionIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflectively set the fixed raw id on a {@link FabricDimensionType}.
|
||||
*
|
||||
* @see FabricDimensionType#getRawId()
|
||||
*/
|
||||
private static void setFixedRawId(FabricDimensionType fabricDimension, int rawId) {
|
||||
try {
|
||||
FABRIC_DIMENSION_TYPE$RAW_ID.setInt(fabricDimension, rawId);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException("Failed to fix a raw id on a FabricDimensionType", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Packet<?> createPacket(DimensionIdsHolder dimensionIdsHolder) {
|
||||
PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
|
||||
buf.writeCompoundTag(dimensionIdsHolder.fabric_getDimensionIds());
|
||||
return ServerSidePacketRegistry.INSTANCE.toPacket(ID, buf);
|
||||
}
|
||||
|
||||
static {
|
||||
try {
|
||||
FABRIC_DIMENSION_TYPE$RAW_ID = FabricDimensionType.class.getDeclaredField("fixedRawId");
|
||||
FABRIC_DIMENSION_TYPE$RAW_ID.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.dimension;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
/**
|
||||
* An object holding a raw id -> full id map for fabric dimensions.
|
||||
*/
|
||||
public interface DimensionIdsHolder {
|
||||
CompoundTag fabric_getDimensionIds();
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.dimension;
|
||||
|
||||
import net.fabricmc.fabric.impl.registry.sync.RemapException;
|
||||
|
||||
public class DimensionRemapException extends RuntimeException {
|
||||
public DimensionRemapException(String message, RemapException cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.dimension;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.text.LiteralText;
|
||||
|
||||
import net.fabricmc.fabric.api.network.ClientSidePacketRegistry;
|
||||
import net.fabricmc.fabric.api.network.PacketContext;
|
||||
import net.fabricmc.fabric.impl.registry.sync.RemapException;
|
||||
|
||||
/**
|
||||
* Client entry point for fabric-dimensions.
|
||||
*/
|
||||
public final class FabricDimensionClientInit {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
public static void onClientInit() {
|
||||
ClientSidePacketRegistry.INSTANCE.register(DimensionIdsFixer.ID, (ctx, buf) -> {
|
||||
CompoundTag compound = buf.readCompoundTag();
|
||||
|
||||
ctx.getTaskQueue().execute(() -> {
|
||||
if (compound == null) {
|
||||
handleError(ctx, new RemapException("Received null compound tag in dimension sync packet!"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
DimensionIdsFixer.apply(compound);
|
||||
} catch (RemapException e) {
|
||||
handleError(ctx, e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static void handleError(PacketContext ctx, Exception e) {
|
||||
LOGGER.error("Dimension id remapping failed!", e);
|
||||
|
||||
MinecraftClient.getInstance().execute(() -> ((ClientPlayerEntity) ctx.getPlayer()).networkHandler.getConnection().disconnect(
|
||||
new LiteralText("Dimension id remapping failed: " + e)
|
||||
));
|
||||
}
|
||||
}
|
|
@ -16,18 +16,19 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.dimension;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import net.minecraft.block.pattern.BlockPattern;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import net.fabricmc.fabric.api.dimension.v1.EntityPlacer;
|
||||
import net.fabricmc.fabric.api.dimension.v1.FabricDimensionType;
|
||||
import net.fabricmc.fabric.api.dimension.v1.FabricDimensions;
|
||||
import net.fabricmc.fabric.mixin.dimension.EntityHooks;
|
||||
|
||||
|
@ -36,15 +37,14 @@ public final class FabricDimensionInternals {
|
|||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static final boolean DEBUG = System.getProperty("fabric.dimension.debug", "false").equalsIgnoreCase("true");
|
||||
public static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final Map<RegistryKey<World>, EntityPlacer> DEFAULT_PLACERS = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The entity currently being transported to another dimension.
|
||||
*/
|
||||
private static final ThreadLocal<Entity> PORTAL_ENTITY = new ThreadLocal<>();
|
||||
/**
|
||||
* The custom placement logic passed from {@link FabricDimensions#teleport(Entity, DimensionType, EntityPlacer)}.
|
||||
* The custom placement logic passed from {@link FabricDimensions#teleport(Entity, ServerWorld, EntityPlacer)}.
|
||||
*/
|
||||
private static EntityPlacer customPlacement;
|
||||
|
||||
|
@ -100,13 +100,13 @@ public final class FabricDimensionInternals {
|
|||
}
|
||||
|
||||
// Default placement logic, falls back to vanilla if not a fabric dimension
|
||||
DimensionType dimType = destination.getDimension().getType();
|
||||
RegistryKey<World> registryKey = destination.getRegistryKey();
|
||||
|
||||
if (dimType instanceof FabricDimensionType) {
|
||||
BlockPattern.TeleportTarget defaultTarget = ((FabricDimensionType) dimType).getDefaultPlacement().placeEntity(teleported, destination, portalDir, portalX, portalY);
|
||||
if (DEFAULT_PLACERS.containsKey(registryKey)) {
|
||||
BlockPattern.TeleportTarget defaultTarget = DEFAULT_PLACERS.get(registryKey).placeEntity(teleported, destination, portalDir, portalX, portalY);
|
||||
|
||||
if (defaultTarget == null) {
|
||||
throw new IllegalStateException("Mod dimension " + DimensionType.getId(dimType) + " returned an invalid teleport target");
|
||||
throw new IllegalStateException("Mod dimension " + destination.getRegistryKey().getValue().toString() + " returned an invalid teleport target");
|
||||
}
|
||||
|
||||
return defaultTarget;
|
||||
|
@ -117,7 +117,7 @@ public final class FabricDimensionInternals {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E extends Entity> E changeDimension(E teleported, DimensionType dimension, EntityPlacer placement) {
|
||||
public static <E extends Entity> E changeDimension(E teleported, ServerWorld dimension, EntityPlacer placement) {
|
||||
assert !teleported.world.isClient : "Entities can only be teleported on the server side";
|
||||
assert Thread.currentThread() == ((ServerWorld) teleported.world).getServer().getThread() : "Entities must be teleported from the main server thread";
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
|
||||
import net.fabricmc.fabric.impl.dimension.FabricDimensionInternals;
|
||||
|
||||
|
@ -30,7 +30,7 @@ import net.fabricmc.fabric.impl.dimension.FabricDimensionInternals;
|
|||
public abstract class MixinEntity {
|
||||
// Inject right before the direction vector is retrieved by the game
|
||||
@Inject(method = "changeDimension", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getLastNetherPortalDirectionVector()Lnet/minecraft/util/math/Vec3d;"))
|
||||
private void onGetPortal(DimensionType dimension, CallbackInfoReturnable<Entity> cir) {
|
||||
private void onGetPortal(ServerWorld targetWorld, CallbackInfoReturnable<Entity> cir) {
|
||||
FabricDimensionInternals.prepareDimensionalTeleportation((Entity) (Object) this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,12 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.dimension.Dimension;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
@Mixin(World.class)
|
||||
public abstract class MixinWorld {
|
||||
@Shadow
|
||||
public abstract Dimension getDimension();
|
||||
public abstract DimensionType getDimension();
|
||||
|
||||
@Shadow
|
||||
private int ambientDarkness;
|
||||
|
@ -44,14 +44,14 @@ public abstract class MixinWorld {
|
|||
*/
|
||||
@Inject(method = "isDay", at = @At("HEAD"), cancellable = true)
|
||||
private void isDay(CallbackInfoReturnable<Boolean> infoReturnable) {
|
||||
if (getDimension().hasVisibleSky()) {
|
||||
if (getDimension().hasSkyLight()) {
|
||||
infoReturnable.setReturnValue(ambientDarkness < 4);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "isNight", at = @At("HEAD"), cancellable = true)
|
||||
private void isNight(CallbackInfoReturnable<Boolean> infoReturnable) {
|
||||
if (getDimension().hasVisibleSky()) {
|
||||
if (getDimension().hasSkyLight()) {
|
||||
infoReturnable.setReturnValue(!(ambientDarkness < 4));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.dimension.idremap;
|
||||
|
||||
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 net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
|
||||
// NOTE: This probably goes into dimension-fixes
|
||||
@Mixin(DimensionType.class)
|
||||
public abstract class MixinDimensionRawIndexFix {
|
||||
@Inject(at = @At("RETURN"), method = "byRawId", cancellable = true)
|
||||
private static void byRawId(final int id, final CallbackInfoReturnable<DimensionType> info) {
|
||||
if (info.getReturnValue() == null || info.getReturnValue().getRawId() != id) {
|
||||
for (DimensionType dimension : Registry.DIMENSION_TYPE) {
|
||||
if (dimension.getRawId() == id) {
|
||||
info.setReturnValue(dimension);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.dimension.idremap;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
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 net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.LevelProperties;
|
||||
|
||||
import net.fabricmc.fabric.impl.dimension.DimensionIdsFixer;
|
||||
import net.fabricmc.fabric.impl.dimension.DimensionIdsHolder;
|
||||
import net.fabricmc.fabric.impl.dimension.DimensionRemapException;
|
||||
import net.fabricmc.fabric.impl.registry.sync.RemapException;
|
||||
|
||||
@Mixin(LevelProperties.class)
|
||||
public abstract class MixinLevelProperties implements DimensionIdsHolder {
|
||||
@Unique
|
||||
private CompoundTag fabricDimensionIds = new CompoundTag();
|
||||
|
||||
@Override
|
||||
public CompoundTag fabric_getDimensionIds() {
|
||||
return fabricDimensionIds;
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(Lnet/minecraft/nbt/CompoundTag;Lcom/mojang/datafixers/DataFixer;ILnet/minecraft/nbt/CompoundTag;)V", at = @At("RETURN"))
|
||||
private void readDimensionIds(CompoundTag data, DataFixer fixer, int version, CompoundTag player, CallbackInfo ci) {
|
||||
CompoundTag savedIds = data.getCompound("fabric_DimensionIds");
|
||||
|
||||
try {
|
||||
this.fabricDimensionIds = DimensionIdsFixer.apply(savedIds);
|
||||
} catch (RemapException e) {
|
||||
throw new DimensionRemapException("Failed to assign unique dimension ids!", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "updateProperties", at = @At("RETURN"))
|
||||
private void writeDimensionIds(CompoundTag data, CompoundTag player, CallbackInfo ci) {
|
||||
data.put("fabric_DimensionIds", fabricDimensionIds);
|
||||
}
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.dimension.idremap;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
|
||||
import net.fabricmc.fabric.impl.dimension.DimensionRemapException;
|
||||
|
||||
@Mixin(LevelStorage.class)
|
||||
public abstract class MixinLevelStorage {
|
||||
@ModifyArg(method = "readLevelProperties(Ljava/io/File;Lcom/mojang/datafixers/DataFixer;)Lnet/minecraft/class_5219;", at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false), index = 2)
|
||||
private static Object disableRecovery(Object e) {
|
||||
if (e instanceof DimensionRemapException) {
|
||||
throw (DimensionRemapException) e;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.dimension.idremap;
|
||||
|
||||
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 net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import net.fabricmc.fabric.impl.dimension.DimensionIdsFixer;
|
||||
import net.fabricmc.fabric.impl.dimension.FabricDimensionInternals;
|
||||
import net.fabricmc.fabric.impl.dimension.DimensionIdsHolder;
|
||||
|
||||
@Mixin(PlayerManager.class)
|
||||
public abstract class MixinPlayerManager {
|
||||
/**
|
||||
* Synchronizes raw dimension ids to connecting players.
|
||||
*/
|
||||
@Inject(method = "onPlayerConnect", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/DifficultyS2CPacket;<init>(Lnet/minecraft/world/Difficulty;Z)V"))
|
||||
private void onPlayerConnect(ClientConnection conn, ServerPlayerEntity player, CallbackInfo info) {
|
||||
// TODO: Refactor out into network + move dimension hook to event
|
||||
|
||||
// No need to send the packet if the player is using the same game instance (dimension types are static)
|
||||
if (!player.server.isSinglePlayer() || !conn.isLocal() || FabricDimensionInternals.DEBUG) {
|
||||
if (player.world.getLevelProperties() instanceof DimensionIdsHolder) {
|
||||
player.networkHandler.sendPacket(DimensionIdsFixer.createPacket((DimensionIdsHolder) player.world.getLevelProperties()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.dimension.idremap;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.UnmodifiableLevelProperties;
|
||||
import net.minecraft.class_5268;
|
||||
|
||||
import net.fabricmc.fabric.impl.dimension.DimensionIdsHolder;
|
||||
|
||||
@Mixin(UnmodifiableLevelProperties.class)
|
||||
public abstract class MixinUnmodifiableLevelProperties implements DimensionIdsHolder {
|
||||
@Shadow
|
||||
@Final
|
||||
private class_5268 properties;
|
||||
|
||||
/**
|
||||
* Delegates to the main level properties.
|
||||
*/
|
||||
@Override
|
||||
public CompoundTag fabric_getDimensionIds() {
|
||||
return ((DimensionIdsHolder) this.properties).fabric_getDimensionIds();
|
||||
}
|
||||
}
|
|
@ -6,12 +6,7 @@
|
|||
"EntityHooks",
|
||||
"MixinEntity",
|
||||
"MixinPortalForcer",
|
||||
"MixinWorld",
|
||||
"idremap.MixinDimensionRawIndexFix",
|
||||
"idremap.MixinLevelProperties",
|
||||
"idremap.MixinLevelStorage",
|
||||
"idremap.MixinPlayerManager",
|
||||
"idremap.MixinUnmodifiableLevelProperties"
|
||||
"MixinWorld"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -5,15 +5,11 @@
|
|||
"license": "Apache-2.0",
|
||||
"depends": {
|
||||
"fabricloader": ">=0.4.0",
|
||||
"minecraft": ">=1.16-rc.3",
|
||||
"fabric-api-base": "*",
|
||||
"fabric-registry-sync-v0": "*",
|
||||
"fabric-networking-v0": "*"
|
||||
},
|
||||
"entrypoints": {
|
||||
"client": [
|
||||
"net.fabricmc.fabric.impl.dimension.FabricDimensionClientInit::onClientInit"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"fabric-dimensions-v1.mixins.json"
|
||||
]
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.dimension;
|
||||
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.pattern.BlockPattern;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.dimension.v1.FabricDimensions;
|
||||
|
||||
public class FabricDimensionTest implements ModInitializer {
|
||||
private static RegistryKey<World> dimensionRegistryKey;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
Registry.register(Registry.CHUNK_GENERATOR, new Identifier("fabric_dimension", "void"), VoidChunkGenerator.CODEC);
|
||||
|
||||
dimensionRegistryKey = RegistryKey.of(Registry.DIMENSION, new Identifier("fabric_dimension", "void"));
|
||||
|
||||
FabricDimensions.registerDefaultPlacer(dimensionRegistryKey, FabricDimensionTest::placeEntityInVoid);
|
||||
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) ->
|
||||
dispatcher.register(literal("fabric_dimension_test").executes(FabricDimensionTest.this::executeTestCommand))
|
||||
);
|
||||
}
|
||||
|
||||
private int executeTestCommand(CommandContext<ServerCommandSource> context) throws CommandSyntaxException {
|
||||
ServerPlayerEntity serverPlayerEntity = context.getSource().getPlayer();
|
||||
ServerWorld serverWorld = serverPlayerEntity.getServerWorld();
|
||||
|
||||
if (!serverWorld.getRegistryKey().equals(dimensionRegistryKey)) {
|
||||
serverPlayerEntity.changeDimension(context.getSource().getMinecraftServer().getWorld(dimensionRegistryKey));
|
||||
} else {
|
||||
FabricDimensions.teleport(serverPlayerEntity, context.getSource().getMinecraftServer().getWorld(World.OVERWORLD), FabricDimensionTest::placeEntity);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static BlockPattern.TeleportTarget placeEntity(Entity teleported, ServerWorld destination, Direction portalDir, double horizontalOffset, double verticalOffset) {
|
||||
return new BlockPattern.TeleportTarget(new Vec3d(0, 100, 0), Vec3d.ZERO, 0);
|
||||
}
|
||||
|
||||
private static BlockPattern.TeleportTarget placeEntityInVoid(Entity teleported, ServerWorld destination, Direction portalDir, double horizontalOffset, double verticalOffset) {
|
||||
destination.setBlockState(new BlockPos(0, 100, 0), Blocks.DIAMOND_BLOCK.getDefaultState());
|
||||
destination.setBlockState(new BlockPos(0, 101, 0), Blocks.TORCH.getDefaultState());
|
||||
return new BlockPattern.TeleportTarget(new Vec3d(0.5, 101, 0.5), Vec3d.ZERO, 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.dimension;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.ChunkRegion;
|
||||
import net.minecraft.world.Heightmap;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import net.minecraft.world.biome.source.BiomeSource;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.gen.StructureAccessor;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.gen.chunk.StructuresConfig;
|
||||
import net.minecraft.world.gen.chunk.VerticalBlockSample;
|
||||
|
||||
public class VoidChunkGenerator extends ChunkGenerator {
|
||||
// Just an example of adding a custom boolean
|
||||
protected final boolean customBool;
|
||||
|
||||
public static final Codec<VoidChunkGenerator> CODEC = RecordCodecBuilder.create((instance) ->
|
||||
instance.group(
|
||||
BiomeSource.field_24713.fieldOf("biome_source")
|
||||
.forGetter((generator) -> generator.biomeSource),
|
||||
Codec.BOOL.fieldOf("custom_bool")
|
||||
.forGetter((generator) -> generator.customBool)
|
||||
)
|
||||
.apply(instance, instance.stable(VoidChunkGenerator::new))
|
||||
);
|
||||
|
||||
public VoidChunkGenerator(BiomeSource biomeSource, boolean customBool) {
|
||||
super(biomeSource, new StructuresConfig(false));
|
||||
this.customBool = customBool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Codec<? extends ChunkGenerator> method_28506() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkGenerator withSeed(long seed) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildSurface(ChunkRegion region, Chunk chunk) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void populateNoise(WorldAccess world, StructureAccessor accessor, Chunk chunk) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight(int x, int z, Heightmap.Type heightmapType) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockView getColumnSample(int x, int z) {
|
||||
return new VerticalBlockSample(new BlockState[0]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"generator": {
|
||||
"type": "fabric_dimension:void",
|
||||
"custom_bool": true,
|
||||
"biome_source": {
|
||||
"type": "minecraft:fixed",
|
||||
"biome": "minecraft:plains"
|
||||
}
|
||||
},
|
||||
"type": "fabric_dimension:void_type"
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"ultrawarm": false,
|
||||
"natural": false,
|
||||
"shrunk": false,
|
||||
"ambient_light": 0.1,
|
||||
"has_skylight": true,
|
||||
"has_ceiling": false,
|
||||
"infiniburn": "minecraft:infiniburn_overworld",
|
||||
"logical_height" : 256,
|
||||
"has_raids" : false,
|
||||
"respawn_anchor_works": false,
|
||||
"bed_works" : false,
|
||||
"piglin_safe" : false
|
||||
}
|
16
fabric-dimensions-v1/src/testmod/resources/fabric.mod.json
Normal file
16
fabric-dimensions-v1/src/testmod/resources/fabric.mod.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "fabric-dimensions-v1-testmod",
|
||||
"name": "Fabric Dimensions (v1) Test Mod",
|
||||
"version": "1.0.0",
|
||||
"environment": "*",
|
||||
"license": "Apache-2.0",
|
||||
"depends": {
|
||||
"fabric-dimensions-v1": "*"
|
||||
},
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.dimension.FabricDimensionTest"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@ include 'fabric-command-api-v1'
|
|||
include 'fabric-containers-v0'
|
||||
include 'fabric-content-registries-v0'
|
||||
include 'fabric-crash-report-info-v1'
|
||||
include 'fabric-dimensions-v1'
|
||||
include 'fabric-events-interaction-v0'
|
||||
include 'fabric-events-lifecycle-v0'
|
||||
include 'fabric-item-groups-v0'
|
||||
|
@ -44,5 +45,4 @@ include 'fabric-rendering-fluids-v1'
|
|||
include 'fabric-resource-loader-v0'
|
||||
include 'fabric-tag-extensions-v0'
|
||||
include 'fabric-textures-v0'
|
||||
//include 'fabric-dimensions-v1'
|
||||
include 'fabric-tool-attribute-api-v1'
|
||||
|
|
Loading…
Add table
Reference in a new issue