Skip to content
This repository has been archived by the owner on Apr 28, 2020. It is now read-only.

Commit

Permalink
Multidimension support
Browse files Browse the repository at this point in the history
  • Loading branch information
Runemoro committed Aug 5, 2018
1 parent 4bd8387 commit d097fd0
Show file tree
Hide file tree
Showing 13 changed files with 357 additions and 22 deletions.
30 changes: 30 additions & 0 deletions src/debug/java/org/dimdev/testmod/ChangeDimensionCommand.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.dimdev.testmod;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity;

public class ChangeDimensionCommand {
public static void register(CommandDispatcher<CommandSource> dispatcher) {
LiteralArgumentBuilder<CommandSource> builder = literalArgument("changedimension")
.then(requiredArgument("dimension", IntegerArgumentType.integer(0))
.executes(context -> {
Entity entity = context.getSource().getEntity();
entity.changeDimension(context.getArgument("dimension", Integer.class));
return 0;
}));
dispatcher.register(builder);
}

private static LiteralArgumentBuilder<CommandSource> literalArgument(String name) {
return LiteralArgumentBuilder.literal(name);
}

private static <T> RequiredArgumentBuilder<CommandSource, T> requiredArgument(String name, ArgumentType<T> type) {
return RequiredArgumentBuilder.argument(name, type);
}
}
18 changes: 13 additions & 5 deletions src/debug/java/org/dimdev/testmod/TestMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,35 @@
import net.minecraft.command.CommandSource;
import net.minecraft.fluid.FlowingFluid;
import net.minecraft.fluid.Fluid;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.EnumDyeColor;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.network.EnumPacketDirection;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.dimension.EndDimension;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dimdev.rift.listener.*;
import org.dimdev.rift.listener.client.AmbientMusicTypeProvider;
import org.dimdev.rift.listener.client.ClientTickable;
import org.dimdev.rift.listener.client.TextureAdder;

import java.util.Collection;
import java.util.Collections;
import java.util.Set;

import static net.minecraft.init.SoundEvents.*;
import static org.dimdev.rift.listener.AmbientMusicTypeProvider.newMusicType;
import static net.minecraft.init.SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP;

public class TestMod implements BlockAdder, ItemAdder, FluidAdder, TextureAdder, PacketAdder, CommandAdder, ClientTickable, AmbientMusicTypeProvider {
public class TestMod implements BlockAdder, ItemAdder, FluidAdder, TextureAdder, PacketAdder, CommandAdder, ClientTickable, AmbientMusicTypeProvider, DimensionTypeAdder {
private static final Logger LOGGER = LogManager.getLogger();
public static final Block WHITE_BLOCK = new Block(Block.Builder.create(Material.ROCK));
public static final Block TRANSLUCENT_WHITE_BLOCK = new BlockStainedGlass(EnumDyeColor.WHITE, Block.Builder.create(Material.GLASS));
public static final FlowingFluid WHITE_FLUID = new WhiteFluid.Source();
public static final FlowingFluid FLOWING_WHITE_FLUID = new WhiteFluid.Flowing();
public static final BlockFlowingFluid BLOCK_WHITE_FLUID = new BlockFlowingFluid(WHITE_FLUID, Block.Builder.create(Material.WATER).doesNotBlockMovement().hardnessAndResistance(100F, 100F).variableOpacity());
public static final Item PACKET_TESTER = new ItemPacketTester(new Item.Builder());
public static final MusicTicker.MusicType TEST_MUSIC = newMusicType("test", ENTITY_EXPERIENCE_ORB_PICKUP, 0,0);
public static final MusicTicker.MusicType TEST_MUSIC = AmbientMusicTypeProvider.newMusicType("test", ENTITY_EXPERIENCE_ORB_PICKUP, 0, 0);
private int clientTickCount = 0;

@Override
Expand Down Expand Up @@ -81,6 +83,7 @@ public void registerLoginPackets(PacketRegistrationReceiver receiver) {}
@Override
public void registerCommands(CommandDispatcher<CommandSource> dispatcher) {
ExplosionCommand.register(dispatcher);
ChangeDimensionCommand.register(dispatcher);
}

@Override
Expand All @@ -94,4 +97,9 @@ public void clientTick() {
public MusicTicker.MusicType getAmbientMusicType(Minecraft client) {
return TEST_MUSIC;
}

@Override
public Set<? extends DimensionType> getDimensionTypes() {
return Collections.singleton(DimensionTypeAdder.newDimensionType(555, "test_dimension", "_test", EndDimension::new));
}
}
16 changes: 16 additions & 0 deletions src/main/java/org/dimdev/rift/listener/DimensionTypeAdder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.dimdev.rift.listener;

import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.dimension.DimensionType;
import org.dimdev.utils.ReflectionUtils;

import java.util.Set;
import java.util.function.Supplier;

public interface DimensionTypeAdder {
static DimensionType newDimensionType(int id, String name, String suffix, Supplier<? extends Dimension> dimensionSupplier) {
return ReflectionUtils.makeEnumInstance(DimensionType.class, new Object[]{name.toUpperCase(), -1, id, name, suffix, dimensionSupplier});
}

Set<? extends DimensionType> getDimensionTypes();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.dimdev.rift.listener.client;

import net.minecraft.client.Minecraft;
import net.minecraft.util.SoundEvent;
import org.dimdev.utils.ReflectionUtils;

import static net.minecraft.client.audio.MusicTicker.MusicType;

public interface AmbientMusicTypeProvider {
static MusicType newMusicType(String name, SoundEvent sound, int minDelay, int maxDelay) {
return ReflectionUtils.makeEnumInstance(MusicType.class, new Object[] {name, -1, sound, minDelay, maxDelay });
}

MusicType getAmbientMusicType(Minecraft client);
}
13 changes: 13 additions & 0 deletions src/main/java/org/dimdev/rift/mixin/core/MixinMinecraftServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.dimdev.rift.mixin.core;

import net.minecraft.server.MinecraftServer;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;

@Mixin(MinecraftServer.class)
public class MixinMinecraftServer {
@Overwrite
public String getServerModName() {
return "rift";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.dimdev.rift.mixin.hook;

import com.mojang.datafixers.DataFixer;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
import net.minecraft.world.chunk.storage.AnvilSaveHandler;
import net.minecraft.world.chunk.storage.IChunkLoader;
import net.minecraft.world.dimension.Dimension;
import net.minecraft.world.storage.SaveHandler;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;

import javax.annotation.Nullable;
import java.io.File;

@Mixin(AnvilSaveHandler.class)
public class MixinAnvilSaveHandler extends SaveHandler {
public MixinAnvilSaveHandler(File p_i49566_1_, String p_i49566_2_, @Nullable MinecraftServer p_i49566_3_, DataFixer p_i49566_4_) {
super(p_i49566_1_, p_i49566_2_, p_i49566_3_, p_i49566_4_);
}

@Overwrite
@Override
public IChunkLoader getChunkLoader(Dimension provider) {
File worldDirectory = getWorldDirectory();
int dimensionId = provider.getDimensionType().getId();
if (dimensionId == 0) {
return new AnvilChunkLoader(worldDirectory, dataFixer);
} else {
File dimensionFolder = new File(worldDirectory, "DIM" + dimensionId);
dimensionFolder.mkdirs();
return new AnvilChunkLoader(dimensionFolder, dataFixer);
}
}
}
37 changes: 37 additions & 0 deletions src/main/java/org/dimdev/rift/mixin/hook/MixinDimensionType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.dimdev.rift.mixin.hook;

import net.minecraft.world.dimension.DimensionType;
import org.dimdev.rift.listener.DimensionTypeAdder;
import org.dimdev.riftloader.RiftLoader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;

import java.util.HashMap;

@Mixin(DimensionType.class)
public class MixinDimensionType {
@SuppressWarnings("PublicStaticMixinMember") // accessed using reflection
private static HashMap<Integer, DimensionType> dimensionTypes = new HashMap<>();

static {
for (DimensionType dimensionType : DimensionType.values()) {
dimensionTypes.put(dimensionType.getId(), dimensionType);
}

for (DimensionTypeAdder dimensionTypeAdder : RiftLoader.instance.getListeners(DimensionTypeAdder.class)) {
for (DimensionType dimensionType : dimensionTypeAdder.getDimensionTypes()) {
dimensionTypes.put(dimensionType.getId(), dimensionType);
}
}
}

@Overwrite
public static DimensionType getById(int id) {
DimensionType dimensionType = dimensionTypes.get(id);
if (dimensionType == null) {
throw new IllegalArgumentException("Invalid dimension id " + id);
}

return dimensionType;
}
}
150 changes: 148 additions & 2 deletions src/main/java/org/dimdev/rift/mixin/hook/MixinMinecraftServer.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,66 @@
package org.dimdev.rift.mixin.hook;

import com.google.gson.JsonElement;
import net.minecraft.profiler.Profiler;
import net.minecraft.resources.IPackFinder;
import net.minecraft.resources.ResourcePackInfo;
import net.minecraft.resources.ResourcePackList;
import net.minecraft.server.CustomBossEvents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.PlayerList;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.*;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.storage.ISaveFormat;
import net.minecraft.world.storage.ISaveHandler;
import net.minecraft.world.storage.WorldInfo;
import org.dimdev.rift.listener.DataPackFinderAdder;
import org.dimdev.rift.listener.ServerTickable;
import org.dimdev.riftloader.RiftLoader;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import javax.annotation.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Mixin(MinecraftServer.class)
public class MixinMinecraftServer {
public abstract class MixinMinecraftServer {
@Shadow @Final private ResourcePackList<ResourcePackInfo> resourcePacks;

@Shadow @Final public Profiler profiler;
@Shadow public abstract void convertMapIfNeeded(String p_convertMapIfNeeded_1_);
@Shadow public WorldServer[] worlds;
@Shadow public long[][] timeOfLastDimensionTick;
@Shadow public abstract ISaveFormat getActiveAnvilConverter();
@Shadow public abstract void setResourcePackFromWorld(String p_setResourcePackFromWorld_1_, ISaveHandler p_setResourcePackFromWorld_2_);
@Shadow public abstract String getFolderName();
@Shadow protected abstract void func_200245_b(ITextComponent p_200245_1_);
@Shadow public abstract GameType getGameType();
@Shadow public abstract boolean canStructuresSpawn();
@Shadow public abstract boolean isHardcore();
@Shadow public abstract void func_195560_a(File p_195560_1_, WorldInfo p_195560_2_);
@Shadow public abstract void initialWorldChunkLoad();
@Shadow public abstract void setDifficultyForAllWorlds(EnumDifficulty p_setDifficultyForAllWorlds_1_);
@Shadow public abstract CustomBossEvents getCustomBossEvents();
@Shadow public abstract PlayerList getPlayerList();
@Shadow public abstract boolean isDemo();
@Shadow public abstract boolean isSinglePlayer();
@Shadow public abstract EnumDifficulty getDifficulty();
@Shadow private boolean enableBonusChest;

private Map<DimensionType, Integer> dimensionTypeToWorldIndex = new HashMap<>();
private Map<Integer, Integer> dimensionIdToWorldIndex = new HashMap<>();

@Inject(method = "func_195560_a", at = @At(value = "INVOKE", target = "Lnet/minecraft/resources/ResourcePackList;addPackFinder(Lnet/minecraft/resources/IPackFinder;)V", shift = At.Shift.AFTER))
private void afterAddVanillaPackFinder(File serverDirectory, WorldInfo worldInfo, CallbackInfo ci) {
for (DataPackFinderAdder resourcePackFinderAdder : RiftLoader.instance.getListeners(DataPackFinderAdder.class)) {
Expand All @@ -42,4 +80,112 @@ private void onTick(CallbackInfo ci) {
}
profiler.endSection();
}

@Overwrite
public void loadAllWorlds(String saveName, String worldName, long seed, WorldType type, JsonElement generatorOptions) {
convertMapIfNeeded(saveName);

func_200245_b(new TextComponentTranslation("menu.loadingLevel"));

ISaveHandler saveHandler = getActiveAnvilConverter().func_197715_a(saveName, (MinecraftServer) (Object) this);
setResourcePackFromWorld(getFolderName(), saveHandler);
WorldInfo worldInfo = saveHandler.loadWorldInfo();
WorldSettings worldSettings = getWorldSettings(worldInfo, seed, type, generatorOptions);

if (worldInfo == null) {
worldInfo = new WorldInfo(worldSettings, worldName);
} else {
worldInfo.setWorldName(worldName);
}

func_195560_a(saveHandler.getWorldDirectory(), worldInfo);

// Create overworld
WorldServer overworld = isDemo() ? new WorldServerDemo((MinecraftServer) (Object) this, saveHandler, worldInfo, 0, profiler)
: new WorldServer((MinecraftServer) (Object) this, saveHandler, worldInfo, 0, profiler);
overworld.init();

overworld.initialize(worldSettings);
overworld.addEventListener(new ServerWorldEventHandler((MinecraftServer) (Object) this, overworld));
if (!isSinglePlayer()) {
overworld.getWorldInfo().setGameType(getGameType());
}

List<WorldServer> worldList = new ArrayList<>();
worldList.add(overworld);
dimensionIdToWorldIndex.put(0, worldList.size());
dimensionTypeToWorldIndex.put(DimensionType.OVERWORLD, worldList.size());

// Create other worlds
List<DimensionType> dimensionTypes;
try {
@SuppressWarnings("JavaReflectionMemberAccess")
Field dimensionTypesField = DimensionType.class.getDeclaredField("dimensionTypes");
dimensionTypesField.setAccessible(true);
//noinspection unchecked
dimensionTypes = new ArrayList<>(((Map<Integer, DimensionType>) dimensionTypesField.get(null)).values());
dimensionTypes.remove(DimensionType.OVERWORLD);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}

for (DimensionType dimensionType : dimensionTypes) {
dimensionIdToWorldIndex.put(dimensionType.getId(), worldList.size());
dimensionTypeToWorldIndex.put(dimensionType, worldList.size());
WorldServerMulti world = new WorldServerMulti((MinecraftServer) (Object) this, saveHandler, dimensionType.getId(), overworld, profiler);
world.init();
world.addEventListener(new ServerWorldEventHandler((MinecraftServer) (Object) this, world));
if (!isSinglePlayer()) {
world.getWorldInfo().setGameType(getGameType());
}

worldList.add(world);
}

// Initialize other things
worlds = worldList.toArray(new WorldServer[0]);
timeOfLastDimensionTick = new long[worlds.length][100];

getPlayerList().setPlayerManager(worlds);
if (worldInfo.func_201357_P() != null) {
getCustomBossEvents().func_201381_a(worldInfo.func_201357_P());
}

if (overworld.getWorldInfo().getDifficulty() == null) {
setDifficultyForAllWorlds(getInitialDifficulty());
}

// initialWorldChunkLoad();
}

protected WorldSettings getWorldSettings(@Nullable WorldInfo worldInfo, long seed, WorldType worldType, JsonElement generatorOptions) {
if (worldInfo == null) {
if (isDemo()) {
return WorldServerDemo.DEMO_WORLD_SETTINGS;
} else {
WorldSettings worldSettings = new WorldSettings(seed, getGameType(), canStructuresSpawn(), isHardcore(), worldType);
worldSettings.func_205390_a(generatorOptions);
if (enableBonusChest) {
worldSettings.enableBonusChest();
}
return worldSettings;
}
} else {
return new WorldSettings(worldInfo);
}
}

protected EnumDifficulty getInitialDifficulty() {
return getDifficulty();
}

@Overwrite
public WorldServer getWorld(DimensionType dimensionType) {
return worlds[dimensionTypeToWorldIndex.get(dimensionType)];
}

@Overwrite
public WorldServer getWorld(int dimensionId) {
return worlds[dimensionIdToWorldIndex.get(dimensionId)];
}
}
Loading

0 comments on commit d097fd0

Please sign in to comment.