chore: add original code

This commit is contained in:
Timo Ley 2023-05-13 14:28:00 +02:00
parent da1131909f
commit b68fd76c8f
42 changed files with 4334 additions and 4 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@
.idea
build
run
bin

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2012 Alex "immibis" Campbell
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -23,9 +23,9 @@ apply plugin: 'maven-publish'
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
version = "1.0"
group= "modgroup"
archivesBaseName = "modid"
version = "0.2"
group= "tinycarts"
archivesBaseName = "com.immibis"
minecraft {
version = "1.7.10-10.13.4.1614-1.7.10"
@ -34,10 +34,16 @@ minecraft {
repositories {
maven { url = "https://maven.tilera.xyz" }
maven {
url = "https://s3.tilera.xyz/cdn/minecraft/libs/"
metadataSources {
artifact()
}
}
}
dependencies {
implementation 'com.immibis:immibis-core:59.1.4:deobf'
}
processResources {

View File

@ -0,0 +1,65 @@
package mods.immibis.subworlds;
import java.util.HashMap;
import java.util.Map;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
// maintains a list of fake entities on the client. there is no server equivalent.
// on the server, fake entities are updated and destroyed by whatever created them.
@SideOnly(Side.CLIENT)
public class ClientFakeEntities {
private static Map<Integer, FakeEntity> ents = new HashMap<Integer, FakeEntity>();
public static void debug(String s) {
System.out.println("[SubWorlds CFE Debug] "+s);
}
public static void add(FakeEntity ent) {
debug("Add CFE "+ent.entityID+": "+ent);
ents.put(ent.entityID, ent);
}
public static FakeEntity remove(int entID) {
debug("Remove CFE "+entID);
return ents.remove(entID);
}
public static void reset() {
debug("Reset CFEs");
ents.clear();
}
public static FakeEntity get(int entID) {
return ents.get(entID);
}
public static void init() {
FMLCommonHandler.instance().bus().register(new ClientTickHandler());
MinecraftForge.EVENT_BUS.register(new EventListener());
}
public static class ClientTickHandler {
@SubscribeEvent
public void onClientTickEnd(TickEvent.ClientTickEvent evt) {
if(evt.phase != TickEvent.Phase.END) return;
for(FakeEntity e : ents.values())
e.tick();
}
}
// must be public or forge throws errors
public static class EventListener {
@SubscribeEvent
public void onRWL(RenderWorldLastEvent evt) {
for(FakeEntity e : ents.values())
e.renderBase(evt.partialTicks);
}
}
}

View File

@ -0,0 +1,27 @@
package mods.immibis.subworlds;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.world.Teleporter;
import net.minecraft.world.WorldServer;
public class ExitTeleporter extends Teleporter {
public WorldServer world;
public double x, y, z;
public ExitTeleporter(WorldServer par1WorldServer, double x, double y, double z) {
super(par1WorldServer);
world = par1WorldServer;
this.x = x;
this.y = y;
this.z = z;
}
@Override
public void placeInPortal(Entity par1Entity, double par2, double par4, double par6, float par8) {
if(par1Entity instanceof EntityLivingBase)
((EntityLivingBase)par1Entity).setPositionAndUpdate(x, y, z);
else
par1Entity.setPosition(x, y, z);
}
}

View File

@ -0,0 +1,110 @@
package mods.immibis.subworlds;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.EntityClientPlayerMP;
import net.minecraft.client.renderer.entity.RenderEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.Packet;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/**
* An object which is synced with clients, like an entity, but isn't actually an entity.
* They are not automatically associated with a world.
*/
public abstract class FakeEntity {
public abstract Packet getUpdatePacket();
public abstract Packet getDescriptionPacket();
public abstract Packet getDestructionPacket();
private static AtomicInteger nextEntityID = new AtomicInteger();
public final int entityID;
public final boolean isClient;
public FakeEntity(boolean isClient) {
entityID = nextEntityID.incrementAndGet();
this.isClient = isClient;
}
public FakeEntity(boolean isClient, int entityID) {
this.entityID = entityID;
this.isClient = isClient;
}
private Set<EntityPlayerMP> trackingPlayers = new HashSet<EntityPlayerMP>();
public final void setTrackingPlayers(Set<EntityPlayerMP> _new) {
for(EntityPlayerMP pl : _new)
if(!trackingPlayers.contains(pl)) {
Packet packet = getDescriptionPacket();
if(packet != null)
pl.playerNetServerHandler.netManager.channel().write(packet);
}
for(EntityPlayerMP pl : trackingPlayers)
if(!_new.contains(pl)) {
Packet packet = getDestructionPacket();
if(packet != null)
pl.playerNetServerHandler.netManager.channel().write(packet);
}
trackingPlayers.clear();
trackingPlayers.addAll(_new);
}
public void tick() {
if(!isClient) {
Packet p = getUpdatePacket();
if(p != null)
for(EntityPlayerMP pl : trackingPlayers)
pl.playerNetServerHandler.netManager.channel().write(p);
}
}
@SideOnly(Side.CLIENT)
protected static RenderEntity defaultRender;
@SideOnly(Side.CLIENT)
protected static Entity defaultRenderEntity;
static {
try {
RenderEntity rd = new RenderEntity();
defaultRender = rd;
defaultRenderEntity = new Entity(null) {
{
boundingBox.setBounds(-1, -1, -1, 1, 1, 1);
lastTickPosX = lastTickPosY = lastTickPosZ = 0;
}
@Override protected void entityInit() {}
@Override protected void readEntityFromNBT(NBTTagCompound var1) {}
@Override protected void writeEntityToNBT(NBTTagCompound var1) {}
};
} catch(Error e) {
}
}
@SideOnly(Side.CLIENT)
// rendering origin is at world coordinates: -pl_x,-pl_y,-pl_z
public void render(double pl_x, double pl_y, double pl_z, float partialTick) {
defaultRender.doRender(defaultRenderEntity, 3, 3, 3, 0, partialTick);
defaultRender.doRender(defaultRenderEntity, -pl_x-2, -pl_y-2, -pl_z-2, 0, partialTick);
}
@SideOnly(Side.CLIENT)
void renderBase(float partialTick) {
EntityClientPlayerMP pl = Minecraft.getMinecraft().thePlayer;
double pl_x = pl.lastTickPosX + (pl.posX - pl.lastTickPosX) * partialTick;
double pl_y = pl.lastTickPosY + (pl.posY - pl.lastTickPosY) * partialTick;
double pl_z = pl.lastTickPosZ + (pl.posZ - pl.lastTickPosZ) * partialTick;
render(pl_x, pl_y, pl_z, partialTick);
}
}

View File

@ -0,0 +1,52 @@
package mods.immibis.subworlds;
import mods.immibis.core.api.porting.SidedProxy;
import mods.immibis.subworlds.dw.DWEntity;
import mods.immibis.subworlds.dw.DWEntityRenderer;
import mods.immibis.subworlds.dw.DWManager;
import mods.immibis.subworlds.mws.MWSManager;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@Mod(version="0.0", dependencies="required-after:ImmibisCore", modid=SubWorldsMod.MODID, name="SubWorlds")
public class SubWorldsMod {
public static final String MODID = "SubWorlds";
@Instance(MODID)
public static SubWorldsMod INSTANCE;
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onClientConnectedToServer(FMLNetworkEvent.ClientConnectedToServerEvent evt) {
ClientFakeEntities.reset();
}
@SideOnly(Side.CLIENT)
public static class ClientProxy {
public ClientProxy() {
ClientFakeEntities.init();
RenderingRegistry.registerEntityRenderingHandler(DWEntity.class, new DWEntityRenderer());
}
}
@EventHandler
public void init(FMLInitializationEvent evt) {
DWManager.init();
MWSManager.init();
FMLCommonHandler.instance().bus().register(this);
SidedProxy.instance.createSidedObject("mods.immibis.subworlds.SubWorldsMod$ClientProxy", null);
EntityRegistry.registerModEntity(DWEntity.class, "detachedWorld", 0, this, 200, 5, true);
}
}

View File

@ -0,0 +1,53 @@
package mods.immibis.subworlds.dw;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import mods.immibis.subworlds.SubWorldsMod;
import net.minecraft.world.World;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.common.ForgeChunkManager.LoadingCallback;
import net.minecraftforge.common.ForgeChunkManager.OrderedLoadingCallback;
import net.minecraftforge.common.ForgeChunkManager.Ticket;
import cpw.mods.fml.relauncher.ReflectionHelper;
public class ChunkLoadingCallback implements LoadingCallback, OrderedLoadingCallback {
@Override
public void ticketsLoaded(List<Ticket> tickets, World world) {
}
@Override
public List<Ticket> ticketsLoaded(List<Ticket> tickets, World world, int maxTicketCount) {
// do not keep chunks loaded through world reloads
return Collections.emptyList();
}
public ChunkLoadingCallback() {
try {
boolean overridesEnabled = ReflectionHelper.getPrivateValue(ForgeChunkManager.class, null, "overridesEnabled");
Map<String, Integer> ticketConstraints = ReflectionHelper.getPrivateValue(ForgeChunkManager.class, null, "ticketConstraints");
Map<String, Integer> chunkConstraints = ReflectionHelper.getPrivateValue(ForgeChunkManager.class, null, "chunkConstraints");
if(!overridesEnabled) {
ticketConstraints.clear();
chunkConstraints.clear();
ReflectionHelper.findField(ForgeChunkManager.class, "overridesEnabled").set(null, true);
}
String modid = SubWorldsMod.MODID;
ticketConstraints.put(modid, Integer.MAX_VALUE);
chunkConstraints.put(modid, Integer.MAX_VALUE);
} catch(Exception e) {
throw new RuntimeException(e);
}
ForgeChunkManager.setForcedChunkLoadingCallback(SubWorldsMod.INSTANCE, this);
}
}

View File

@ -0,0 +1,160 @@
package mods.immibis.subworlds.dw;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.init.Blocks;
import net.minecraft.util.IProgressUpdate;
import net.minecraft.world.ChunkPosition;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
public class DWChunkGenerator implements IChunkProvider {
public static final BiomeGenBase BIOME = BiomeGenBase.plains;
public final DWWorldProvider provider;
private final int HSIZE; // chunks
private WorldServer worldObj;
//private EmptyChunk emptyChunk;
public DWChunkGenerator(WorldServer worldObj, DWWorldProvider provider) {
this.worldObj = worldObj;
this.provider = provider;
this.HSIZE = Math.max(provider.props.zsize, provider.props.xsize) / 16;
//emptyChunk = new EmptyChunk(worldObj, -10000, -10000);
}
@Override
public boolean chunkExists(int var1, int var2) {
return true;
}
@Override
public Chunk provideChunk(int var1, int var2) {
boolean outOfBounds = var1 < 0 || var2 < 0 || var1 >= HSIZE || var2 >= HSIZE;
// if(outOfBounds) return emptyChunk;
Chunk chunk = new Chunk(this.worldObj, var1, var2);
if(!outOfBounds) {
/*for(int y = 0; y < provider.props.vsize; y++) {
if(var1 == 0 && var2 == 0)
chunk.setBlockIDWithMetadata(0, y, 0, Block.stone.blockID, 0);
if(var1 == HSIZE-1 && var2 == 0)
chunk.setBlockIDWithMetadata(15, y, 0, Block.stone.blockID, 0);
if(var1 == 0 && var2 == HSIZE-1)
chunk.setBlockIDWithMetadata(0, y, 15, Block.stone.blockID, 0);
if(var1 == HSIZE-1 && var2 == HSIZE-1)
chunk.setBlockIDWithMetadata(15, y, 15, Block.stone.blockID, 0);
}
for(int x = 0; x < 16; x++) {
if(var1 == 0) {
chunk.setBlockIDWithMetadata(0, 0, x, Block.stone.blockID, 0);
chunk.setBlockIDWithMetadata(0, provider.props.vsize-1, x, Block.stone.blockID, 0);
}
if(var1 == HSIZE-1) {
chunk.setBlockIDWithMetadata(15, 0, x, Block.stone.blockID, 0);
chunk.setBlockIDWithMetadata(15, provider.props.vsize-1, x, Block.stone.blockID, 0);
}
if(var2 == 0) {
chunk.setBlockIDWithMetadata(x, 0, 0, Block.stone.blockID, 0);
chunk.setBlockIDWithMetadata(x, provider.props.vsize-1, 0, Block.stone.blockID, 0);
}
if(var2 == HSIZE-1) {
chunk.setBlockIDWithMetadata(x, 0, 15, Block.stone.blockID, 0);
chunk.setBlockIDWithMetadata(x, provider.props.vsize-1, 15, Block.stone.blockID, 0);
}
}
for(int x = 0; x < 16; x++)
for(int z = 0; z < 16; z++)
chunk.setBlockIDWithMetadata(x, 0, z, Block.stone.blockID, 0);*/
int nx = provider.props.xsize/2 - var1 * 16;
int nz = provider.props.zsize/2 - var2 * 16;
for(int dx = 0; dx < 2; dx++)
for(int dy = 0; dy < 2; dy++)
for(int dz = 0; dz < 2; dz++) {
int x = nx+dx, y = provider.props.ysize/2+dy, z = nz+dz;
if(x >= 0 && y >= 0 && z >= 0 && x < 16 && y < provider.props.ysize && z < 16)
chunk.func_150807_a(x, y, z, Blocks.stone, 0);
}
}
chunk.generateSkylightMap();
Arrays.fill(chunk.getBiomeArray(), (byte)BIOME.biomeID);
chunk.isTerrainPopulated = true;
return chunk;
}
@Override
public Chunk loadChunk(int var1, int var2) {
return provideChunk(var1, var2);
}
@Override
public void populate(IChunkProvider var1, int var2, int var3) {
}
@Override
public boolean saveChunks(boolean var1, IProgressUpdate var2) {
return true;
}
@Override
public boolean unloadQueuedChunks() {
return false;
}
@Override
public boolean canSave() {
return true;
}
@Override
public String makeString() {
return "DWChunkGenerator";
}
@SuppressWarnings("rawtypes")
@Override
public List getPossibleCreatures(EnumCreatureType var1, int var2, int var3, int var4) {
return Collections.emptyList();
}
@Override
public ChunkPosition func_147416_a(World var1, String var2, int var3, int var4, int var5) {
return null;
}
@Override
public int getLoadedChunkCount() {
return 0;
}
@Override
public void recreateStructures(int var1, int var2) {
}
@Override
public void saveExtraData() {
}
}

View File

@ -0,0 +1,167 @@
package mods.immibis.subworlds.dw;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import mods.immibis.subworlds.FakeEntity;
import mods.immibis.subworlds.mws.MWSListener;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
/**
* DW stands for "Detached World".
* Maximum size is always 48x48x48, this cannot easily be changed.
* There might be smaller, 16x16x16
*/
public abstract class DWEntity extends Entity {
// set of players last known to be tracking this entity which are outside it
private Set<EntityPlayerMP> trackingPlayers = new HashSet<EntityPlayerMP>();
// set of players last known to be inside this entity
private Set<EntityPlayerMP> enclosedPlayers = new HashSet<EntityPlayerMP>();
// fake entity sent to enclosed players to render the external world.
private FakeEntity externalWorldFE;
// internal world, cannot change after initialization
private WorldServer internalWorld;
//private int lastChunkX = Integer.MAX_VALUE, lastChunkZ = Integer.MAX_VALUE;
public DWEntity(World par1World) {
super(par1World);
setSize(48, 48);
}
private static final int WATCHER_INDEX_DIMID = 8;
@Override
protected void entityInit() {
dataWatcher.addObject(WATCHER_INDEX_DIMID, 0);
}
@Override
protected void readEntityFromNBT(NBTTagCompound var1) {
}
@Override
protected void writeEntityToNBT(NBTTagCompound var1) {
}
protected abstract WorldServer getInternalWorld();
protected abstract void updateExternalWorldFE(FakeEntity e);
protected abstract FakeEntity createExternalWorldFE();
protected abstract void applyGLRotation(float partialTick);
private boolean requiresInteriorLoaded = false;
public boolean requiresInteriorLoaded() {return requiresInteriorLoaded;}
/*@Override
public void setPosition(double par1, double par3, double par5) {
}
@Override
public void setPositionAndRotation(double par1, double par3, double par5, float par7, float par8) {};
*/
@Override
public void onUpdate() {
this.prevPosX = this.posX;
this.prevPosY = this.posY;
this.prevPosZ = this.posZ;
/*if(worldObj.isRemote) {
posX = serverPosX / 32.0;
posY = serverPosY / 32.0;
posZ = serverPosZ / 32.0;
}*/
if(!worldObj.isRemote) {
int lastDimID = (internalWorld == null ? 0 : internalWorld.provider.dimensionId);
internalWorld = getInternalWorld();
if(internalWorld.provider.dimensionId != lastDimID)
dataWatcher.updateObject(WATCHER_INDEX_DIMID, internalWorld.provider.dimensionId);
// enclosed players see the outside world via MWS
@SuppressWarnings("unchecked")
Set<EntityPlayerMP> nowEnclosed = new HashSet<EntityPlayerMP>(internalWorld.playerEntities);
for(final EntityPlayerMP pl : nowEnclosed)
if(!enclosedPlayers.contains(pl)) {
MWSManager.getWorldManager(worldObj).addListener(new MWSListener(pl.playerNetServerHandler.netManager) {
@Override
public void update() {
/*x = (int)DWEntity.this.comX;
y = (int)DWEntity.this.comY;
z = (int)DWEntity.this.comZ;*/
x = (int)posX;
y = (int)posY;
z = (int)posZ;
}
});
}
for(final EntityPlayerMP pl : enclosedPlayers)
if(!nowEnclosed.contains(pl))
MWSManager.getWorldManager(worldObj).removeListener(pl.playerNetServerHandler.netManager);
enclosedPlayers = nowEnclosed;
// tracking players see the enclosed world via MWS
Set<EntityPlayerMP> nowTracking = DWUtils.getTrackingPlayers(this);
for(final EntityPlayerMP pl : trackingPlayers)
if(!nowTracking.contains(pl))
MWSManager.getWorldManager(internalWorld).removeListener(pl.playerNetServerHandler.netManager);
trackingPlayers.retainAll(nowTracking);
for(final EntityPlayerMP pl : nowTracking)
if(!trackingPlayers.contains(pl)) {
MWSManager.getWorldManager(internalWorld).addListener(new MWSListener(pl.playerNetServerHandler.netManager) {
@Override public void update() {}
});
trackingPlayers.add(pl);
}
requiresInteriorLoaded = enclosedPlayers.size() > 0 || trackingPlayers.size() > 0;
if(externalWorldFE == null) {
externalWorldFE = createExternalWorldFE();
}
//DWWorldProvider provider = (DWWorldProvider)getInternalWorld().provider;
updateExternalWorldFE(externalWorldFE);
externalWorldFE.setTrackingPlayers(enclosedPlayers);
externalWorldFE.tick();
}
}
@Override
public boolean isInRangeToRenderDist(double distsq) {
return true;
}
public int getEnclosedDimensionClient() {
return dataWatcher.getWatchableObjectInt(WATCHER_INDEX_DIMID);
}
public void onUnloadOrDestroy() {
for(final EntityPlayerMP pl : enclosedPlayers)
MWSManager.getWorldManager(worldObj).removeListener(pl.playerNetServerHandler.netManager);
if(internalWorld != null)
for(final EntityPlayerMP pl : trackingPlayers)
MWSManager.getWorldManager(internalWorld).removeListener(pl.playerNetServerHandler.netManager);
if(externalWorldFE != null)
externalWorldFE.setTrackingPlayers(Collections.<EntityPlayerMP>emptySet());
}
}

View File

@ -0,0 +1,406 @@
package mods.immibis.subworlds.dw;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import mods.immibis.subworlds.mws.MWSClientWorld;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.particle.EffectRenderer;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.culling.ClippingHelperImpl;
import net.minecraft.client.renderer.culling.Frustrum;
import net.minecraft.client.renderer.entity.RenderEntity;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.client.MinecraftForgeClient;
import org.lwjgl.opengl.GL11;
public class DWEntityRenderer extends RenderEntity {
private static List clipperStack = new ArrayList();
private static Field clipperInstanceField;
static {
clipperInstanceField = ClippingHelperImpl.class.getDeclaredFields()[0];
clipperInstanceField.setAccessible(true);
}
public static void pushClipper() {
try {
clipperStack.add(clipperInstanceField.get(null));
clipperInstanceField.set(null, new ClippingHelperImpl());
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public static void popClipper() {
try {
clipperInstanceField.set(null, clipperStack.remove(clipperStack.size() - 1));
} catch(Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void doRender(Entity ent_, double x, double y, double z, float yaw, float partialTick) {
DWEntity ent = (DWEntity)ent_;
double entX = ent.prevPosX + (ent.posX - ent.prevPosX) * partialTick;
double entY = ent.prevPosY + (ent.posY - ent.prevPosY) * partialTick;
double entZ = ent.prevPosZ + (ent.posZ - ent.prevPosZ) * partialTick;
GL11.glPushMatrix();
GL11.glTranslated(x, y, z);
ent.applyGLRotation(partialTick);
int dimID = ent.getEnclosedDimensionClient();
MWSClientWorld subWorld = dimID == 0 ? null : MWSManager.getClientWorld(dimID);
if(subWorld == null)
super.doRender(ent_, 0, 0, 0, yaw, partialTick);
else
{
//System.out.println(ent.hashCode()+"\t"+ent.entityId+" at "+entX+" "+entY+" "+entZ+" "+ent.world.getBlockId(5, 0, 5));
//System.out.println("Render world "+ent.world+" for "+ent.zeppelinID);
//GL11.glTranslated(x - entX, y - entY, z - entZ);
//GL11.glTranslated(entX - x, entY - y, entZ - z);
DWWorldProvider pv = (DWWorldProvider)subWorld.provider;
GL11.glTranslatef(-pv.props.xsize/2.0f, 0, -pv.props.zsize/2.0f);
renderClientWorld(subWorld, partialTick, entX, entY, entZ, 0, 0, 0);
}
GL11.glPopMatrix();
}
// e_*: unused.
// pl_*: centre of rendered area (we pretend the player is here)
// pl_* is also the point in the world that will be rendered at 0,0,0.
public static void renderClientWorld(MWSClientWorld world, float partialTick, double e_x, double e_y, double e_z, double pl_x, double pl_y, double pl_z) {
Minecraft mc = Minecraft.getMinecraft();
GL11.glColor3f(1, 1, 1);
RenderGlobal oldRG = mc.renderGlobal;
EntityRenderer oldER = mc.entityRenderer;
WorldClient oldWorld = mc.theWorld;
mc.renderGlobal = world.render;
mc.entityRenderer = world.erender;
mc.theWorld = world;
int oldPass = MinecraftForgeClient.getRenderPass();
pushClipper();
// Pretend the player is at the origin (of the zeppelin world)
EntityLivingBase pl = mc.renderViewEntity;
double realX = pl.posX, realY = pl.posY, realZ = pl.posZ;
double realPrevX = pl.prevPosX, realPrevY = pl.prevPosY, realPrevZ = pl.prevPosZ;
double realLastX = pl.lastTickPosX, realLastY = pl.lastTickPosY, realLastZ = pl.lastTickPosZ;
pl.posX = pl.prevPosX = pl.lastTickPosX = pl_x;
pl.posY = pl.prevPosY = pl.lastTickPosY = pl_y;
pl.posZ = pl.prevPosZ = pl.lastTickPosZ = pl_z;
er_renderWorld(mc.entityRenderer, partialTick, 0);
popClipper();
ForgeHooksClient.setRenderPass(oldPass);
mc.theWorld = oldWorld;
mc.entityRenderer = oldER;
mc.renderGlobal = oldRG;
pl.posX = realX;
pl.posY = realY;
pl.posZ = realZ;
pl.prevPosX = realPrevX;
pl.prevPosY = realPrevY;
pl.prevPosZ = realPrevZ;
pl.lastTickPosX = realLastX;
pl.lastTickPosY = realLastY;
pl.lastTickPosZ = realLastZ;
}
private static void er_renderWorld(EntityRenderer self, float par1, long par2) {
Minecraft mc = Minecraft.getMinecraft();
mc.mcProfiler.startSection("lightTex");
/*if (self.lightmapUpdateNeeded)
{
self.updateLightmap(par1);
}*/
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glEnable(GL11.GL_DEPTH_TEST);
if (mc.renderViewEntity == null)
{
mc.renderViewEntity = mc.thePlayer;
}
mc.mcProfiler.endStartSection("pick");
//self.getMouseOver(par1);
EntityLivingBase entitylivingbase = mc.renderViewEntity;
RenderGlobal renderglobal = mc.renderGlobal;
EffectRenderer effectrenderer = mc.effectRenderer;
double d0 = entitylivingbase.lastTickPosX + (entitylivingbase.posX - entitylivingbase.lastTickPosX) * (double)par1;
double d1 = entitylivingbase.lastTickPosY + (entitylivingbase.posY - entitylivingbase.lastTickPosY) * (double)par1;
double d2 = entitylivingbase.lastTickPosZ + (entitylivingbase.posZ - entitylivingbase.lastTickPosZ) * (double)par1;
mc.mcProfiler.endStartSection("center");
//for (int j = 0; j < 2; ++j)
//{
/*if (mc.gameSettings.anaglyph)
{
anaglyphField = j;
if (anaglyphField == 0)
{
GL11.glColorMask(false, true, true, false);
}
else
{
GL11.glColorMask(true, false, false, false);
}
}*/
mc.mcProfiler.endStartSection("clear");
//GL11.glViewport(0, 0, mc.displayWidth, mc.displayHeight);
//self.updateFogColor(par1);
//GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
GL11.glEnable(GL11.GL_CULL_FACE);
mc.mcProfiler.endStartSection("camera");
//self.setupCameraTransform(par1, j);
//ActiveRenderInfo.updateRenderInfo(mc.thePlayer, mc.gameSettings.thirdPersonView == 2);
mc.mcProfiler.endStartSection("frustrum");
ClippingHelperImpl.getInstance();
/*if (mc.gameSettings.renderDistance < 2)
{
self.setupFog(-1, par1);
mc.mcProfiler.endStartSection("sky");
renderglobal.renderSky(par1);
}*/
//GL11.glEnable(GL11.GL_FOG);
//self.setupFog(1, par1);
if (mc.gameSettings.ambientOcclusion != 0)
{
GL11.glShadeModel(GL11.GL_SMOOTH);
}
mc.mcProfiler.endStartSection("culling");
Frustrum frustrum = new Frustrum();
frustrum.setPosition(d0, d1, d2);
mc.renderGlobal.clipRenderersByFrustum(frustrum, par1);
//if (j == 0)
//{
mc.mcProfiler.endStartSection("updatechunks");
while (!mc.renderGlobal.updateRenderers(entitylivingbase, false) && par2 != 0L)
{
long k = par2 - System.nanoTime();
if (k < 0L || k > 1000000000L)
{
break;
}
}
//}
//if (entitylivingbase.posY < 128.0D)
//{
// self.renderCloudsCheck(renderglobal, par1);
//}
mc.mcProfiler.endStartSection("prepareterrain");
//self.setupFog(0, par1);
//GL11.glEnable(GL11.GL_FOG);
mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture);
RenderHelper.disableStandardItemLighting();
mc.mcProfiler.endStartSection("terrain");
renderglobal.sortAndRender(entitylivingbase, 0, (double)par1);
GL11.glShadeModel(GL11.GL_FLAT);
EntityPlayer entityplayer;
if (self.debugViewDirection == 0)
{
/* XXX TODO: enable entity rendering inside DW's. renderEntities uses statics that need to be reset afterwards.
RenderHelper.enableStandardItemLighting();
mc.mcProfiler.endStartSection("entities");
ForgeHooksClient.setRenderPass(0);
renderglobal.renderEntities(entitylivingbase.getPosition(par1), frustrum, par1);
ForgeHooksClient.setRenderPass(0);
*/
/* Forge: Moved down
self.enableLightmap((double)par1);
mc.mcProfiler.endStartSection("litParticles");
effectrenderer.renderLitParticles(entitylivingbase, par1);
RenderHelper.disableStandardItemLighting();
self.setupFog(0, par1);
mc.mcProfiler.endStartSection("particles");
effectrenderer.renderParticles(entitylivingbase, par1);
self.disableLightmap((double)par1);
*/
/*if (mc.objectMouseOver != null && entitylivingbase.isInsideOfMaterial(Material.water) && entitylivingbase instanceof EntityPlayer && !mc.gameSettings.hideGUI)
{
entityplayer = (EntityPlayer)entitylivingbase;
GL11.glDisable(GL11.GL_ALPHA_TEST);
mc.mcProfiler.endStartSection("outline");
if (!ForgeHooksClient.onDrawBlockHighlight(renderglobal, entityplayer, mc.objectMouseOver, 0, entityplayer.inventory.getCurrentItem(), par1))
{
renderglobal.drawSelectionBox(entityplayer, mc.objectMouseOver, 0, par1);
}
GL11.glEnable(GL11.GL_ALPHA_TEST);
}*/
}
GL11.glDisable(GL11.GL_BLEND);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
GL11.glDepthMask(true);
//self.setupFog(0, par1);
GL11.glEnable(GL11.GL_BLEND);
GL11.glDisable(GL11.GL_CULL_FACE);
mc.getTextureManager().bindTexture(TextureMap.locationBlocksTexture);
if (mc.gameSettings.fancyGraphics)
{
mc.mcProfiler.endStartSection("water");
if (mc.gameSettings.ambientOcclusion != 0)
{
GL11.glShadeModel(GL11.GL_SMOOTH);
}
GL11.glColorMask(false, false, false, false);
int l = renderglobal.sortAndRender(entitylivingbase, 1, (double)par1);
/*if (mc.gameSettings.anaglyph)
{
if (anaglyphField == 0)
{
GL11.glColorMask(false, true, true, true);
}
else
{
GL11.glColorMask(true, false, false, true);
}
}
else*/
{
GL11.glColorMask(true, true, true, true);
}
if (l > 0)
{
renderglobal.renderAllRenderLists(1, (double)par1);
}
GL11.glShadeModel(GL11.GL_FLAT);
}
else
{
mc.mcProfiler.endStartSection("water");
renderglobal.sortAndRender(entitylivingbase, 1, (double)par1);
}
/* XXX TODO: enable entity rendering inside DW's. renderEntities uses statics that need to be reset afterwards.
if (self.debugViewDirection == 0) //Only render if render pass 0 happens as well.
{
RenderHelper.enableStandardItemLighting();
mc.mcProfiler.endStartSection("entities");
ForgeHooksClient.setRenderPass(1);
renderglobal.renderEntities(entitylivingbase.getPosition(par1), frustrum, par1);
ForgeHooksClient.setRenderPass(-1);
RenderHelper.disableStandardItemLighting();
} */
GL11.glDepthMask(true);
GL11.glEnable(GL11.GL_CULL_FACE);
GL11.glDisable(GL11.GL_BLEND);
RenderHelper.enableStandardItemLighting();
/*if (self.cameraZoom == 1.0D && entitylivingbase instanceof EntityPlayer && !mc.gameSettings.hideGUI && mc.objectMouseOver != null && !entitylivingbase.isInsideOfMaterial(Material.water))
{
entityplayer = (EntityPlayer)entitylivingbase;
GL11.glDisable(GL11.GL_ALPHA_TEST);
mc.mcProfiler.endStartSection("outline");
if (!ForgeHooksClient.onDrawBlockHighlight(renderglobal, entityplayer, mc.objectMouseOver, 0, entityplayer.inventory.getCurrentItem(), par1))
{
renderglobal.drawSelectionBox(entityplayer, mc.objectMouseOver, 0, par1);
}
GL11.glEnable(GL11.GL_ALPHA_TEST);
}*/
//mc.mcProfiler.endStartSection("destroyProgress");
//GL11.glEnable(GL11.GL_BLEND);
//GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE);
//renderglobal.drawBlockDamageTexture(Tessellator.instance, entitylivingbase, par1);
//GL11.glDisable(GL11.GL_BLEND);
//mc.mcProfiler.endStartSection("weather");
//self.renderRainSnow(par1);
//GL11.glDisable(GL11.GL_FOG);
//if (entitylivingbase.posY >= 128.0D)
//{
// self.renderCloudsCheck(renderglobal, par1);
//}
//Forge: Moved section from above, now particles are the last thing to render.
/*self.enableLightmap((double)par1);
mc.mcProfiler.endStartSection("litParticles");
effectrenderer.renderLitParticles(entitylivingbase, par1);
RenderHelper.disableStandardItemLighting();
//self.setupFog(0, par1);
mc.mcProfiler.endStartSection("particles");
effectrenderer.renderParticles(entitylivingbase, par1);
self.disableLightmap((double)par1);*/
//Forge: End Move
//mc.mcProfiler.endStartSection("FRenderLast");
//ForgeHooksClient.dispatchRenderLast(renderglobal, par1);
//mc.mcProfiler.endStartSection("hand");
//if (self.cameraZoom == 1.0D)
//{
// GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
// self.renderHand(par1, j);
//}
//if (!mc.gameSettings.anaglyph)
//{
mc.mcProfiler.endSection();
//return;
//}
//}
//GL11.glColorMask(true, true, true, false);
//mc.mcProfiler.endSection();
}
}

View File

@ -0,0 +1,231 @@
package mods.immibis.subworlds.dw;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mods.immibis.core.api.APILocator;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.core.api.net.IPacketMap;
import mods.immibis.core.api.util.NBTType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.IWorldAccess;
import net.minecraft.world.World;
import net.minecraft.world.WorldSavedData;
import net.minecraft.world.storage.MapStorage;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent;
public class DWManager {
// Note: DimensionManager isn't thread safe.
// We avoid calling it from the client thread when the integrated server is running.
public static final int PROVIDER_TYPE_ID = "SpaceCraftMod".hashCode();
public static final String CHANNEL = "SpaceCraftDW";
public static final byte PKT_DIMENSION_LIST = 0;
private static Map<Integer, WorldProps> registeredDimensions = new HashMap<Integer, WorldProps>();
private static SaveSpecificData getSaveData() {
MapStorage ms = DimensionManager.getWorld(0).mapStorage;
SaveSpecificData ssd = (SaveSpecificData)ms.loadData(SaveSpecificData.class, "subworlds-dw-save-data");
if(ssd == null)
ms.setData("subworlds-dw-save-data", ssd = new SaveSpecificData("subworlds-dw-save-data"));
return ssd;
}
/**
* Creates a new empty dimension for Detached World use,
* returns the dimension ID.
* only called on the server
*/
public static int createWorld(WorldProps props) {
int dim = DimensionManager.getNextFreeDimId();
System.out.println("DWManager: creating dimension #"+dim);
registerDimensions(Collections.singletonMap(dim, props), false);
MinecraftServer.getServer().worldServerForDimension(dim);
System.out.println("DWManager: created dimension #"+dim);
return dim;
}
public static int createWorld() {
return createWorld(new WorldProps());
}
public static void init() {
DimensionManager.registerProviderType(PROVIDER_TYPE_ID, DWWorldProvider.class, false);
MinecraftForge.EVENT_BUS.register(new EventHandler());
FMLCommonHandler.instance().bus().register(new ConnectionHandler());
APILocator.getNetManager().listen(new PacketMap());
new ChunkLoadingCallback();
}
private static class PacketMap implements IPacketMap {
@Override
public String getChannel() {
return CHANNEL;
}
@Override
public IPacket createS2CPacket(byte id) {
if(id == PKT_DIMENSION_LIST)
return new PacketDWDimensionList();
return null;
}
@Override
public IPacket createC2SPacket(byte id) {
return null;
}
}
public static class ConnectionHandler {
@SubscribeEvent
public String connectionReceived(FMLNetworkEvent.ServerConnectionFromClientEvent evt) {
PacketDWDimensionList packet = new PacketDWDimensionList();
packet.clearExisting = true;
packet.data = registeredDimensions;
APILocator.getNetManager().send(packet, evt.manager, true);
return null;
}
}
// only called on the server, unless there is no server
@SuppressWarnings("unchecked")
static void registerDimensions(Map<Integer, WorldProps> dimIDs, boolean clearExisting) {
if(clearExisting) {
for(int i : registeredDimensions.keySet())
DimensionManager.unregisterDimension(i);
registeredDimensions.clear();
}
SaveSpecificData sd = MinecraftServer.getServer() == null ? null : getSaveData();
for(int i : dimIDs.keySet()) {
DimensionManager.registerDimension(i, PROVIDER_TYPE_ID);
registeredDimensions.put(i, dimIDs.get(i));
if(sd != null && !sd.dw_worlds.containsKey(i)) {
sd.dw_worlds.put(i, dimIDs.get(i));
sd.setDirty(true);
}
}
if(sd != null)
{
PacketDWDimensionList packet = new PacketDWDimensionList();
packet.clearExisting = clearExisting;
packet.data = dimIDs;
for(EntityPlayer pl : (List<EntityPlayer>)MinecraftServer.getServer().getConfigurationManager().playerEntityList) {
APILocator.getNetManager().sendToClient(packet, pl);
}
}
}
// must be public or Forge crashes
public static class EventHandler {
@SubscribeEvent
public void onWorldLoad(WorldEvent.Load evt) {
if(!evt.world.isRemote && evt.world.provider.dimensionId == 0) {
registerDimensions(getSaveData().dw_worlds, true);
}
if(!evt.world.isRemote)
evt.world.addWorldAccess(new WorldListener(evt.world));
}
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload evt) {
if(!evt.world.isRemote && evt.world.provider.dimensionId == 0) {
registerDimensions(Collections.<Integer, WorldProps>emptyMap(), true);
}
}
}
// must be public as instances are created by reflection
public static class SaveSpecificData extends WorldSavedData {
public SaveSpecificData(String par1Str) {
super(par1Str);
}
public Map<Integer, WorldProps> dw_worlds = new HashMap<Integer, WorldProps>();
@Override
public void readFromNBT(NBTTagCompound var1) {
if(var1.hasKey("dw_worlds")) {
NBTTagList t = var1.getTagList("dw_worlds", NBTType.COMPOUND);
for(int k = 0; k < t.tagCount(); k++)
{
WorldProps props = new WorldProps();
NBTTagCompound tag = t.getCompoundTagAt(k);
int dim = tag.getInteger("dim");
props.read(tag);
dw_worlds.put(dim, props);
}
}
}
@Override
public void writeToNBT(NBTTagCompound var1) {
NBTTagList t = new NBTTagList();
for(Map.Entry<Integer, WorldProps> i : dw_worlds.entrySet()) {
NBTTagCompound c = new NBTTagCompound();
c.setInteger("dim", i.getKey());
i.getValue().write(c);
t.appendTag(c);
}
var1.setTag("dw_worlds", t);
}
}
private static class WorldListener implements IWorldAccess {
//public final World worldObj;
public WorldListener(World world) {
//this.worldObj = world;
}
@Override
public void onEntityDestroy(Entity var1) {
if(var1 instanceof DWEntity)
((DWEntity)var1).onUnloadOrDestroy();
}
@Override public void markBlockForUpdate(int var1, int var2, int var3) {}
@Override public void markBlockForRenderUpdate(int var1, int var2, int var3) {}
@Override public void markBlockRangeForRenderUpdate(int var1, int var2, int var3, int var4, int var5, int var6) {}
@Override public void playSound(String var1, double var2, double var4, double var6, float var8, float var9) {}
@Override public void playSoundToNearExcept(EntityPlayer var1, String var2, double var3, double var5, double var7, float var9, float var10) {}
@Override public void spawnParticle(String var1, double var2, double var4, double var6, double var8, double var10, double var12) {}
@Override public void onEntityCreate(Entity var1) {}
@Override public void playRecord(String var1, int var2, int var3, int var4) {}
@Override public void broadcastSound(int var1, int var2, int var3, int var4, int var5) {}
@Override public void playAuxSFX(EntityPlayer var1, int var2, int var3, int var4, int var5, int var6) {}
@Override public void destroyBlockPartially(int var1, int var2, int var3, int var4, int var5) {}
@Override public void onStaticEntitiesChanged() {}
}
public static WorldProps getProps(int dim) {
WorldProps p = registeredDimensions.get(dim);
if(p == null)
return new WorldProps(); // probably on client. TODO verify correct
return p;
}
}

View File

@ -0,0 +1,31 @@
package mods.immibis.subworlds.dw;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.world.Teleporter;
import net.minecraft.world.WorldServer;
public class DWTeleporter extends Teleporter {
public WorldServer world;
public DWTeleporter(WorldServer par1WorldServer) {
super(par1WorldServer);
world = par1WorldServer;
}
@Override
public void placeInPortal(Entity par1Entity, double par2, double par4, double par6, float par8) {
DWWorldProvider provider = (DWWorldProvider)world.provider;
double x = provider.props.xsize/2;
double y = 5;
double z = provider.props.zsize/2;
if(par1Entity instanceof EntityLivingBase)
((EntityLivingBase)par1Entity).setPositionAndUpdate(x, y, z);
else
par1Entity.setPosition(x, y, z);
}
}

View File

@ -0,0 +1,22 @@
package mods.immibis.subworlds.dw;
import java.util.Set;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityTracker;
import net.minecraft.entity.EntityTrackerEntry;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.IntHashMap;
import net.minecraft.world.WorldServer;
import cpw.mods.fml.relauncher.ReflectionHelper;
public class DWUtils {
@SuppressWarnings("unchecked")
public static Set<EntityPlayerMP> getTrackingPlayers(Entity ent) {
EntityTracker t = ((WorldServer)ent.worldObj).getEntityTracker();
IntHashMap trackedEntityIDs = ReflectionHelper.getPrivateValue(EntityTracker.class, t, 3);
EntityTrackerEntry entry = (EntityTrackerEntry)trackedEntityIDs.lookup(ent.getEntityId());
return entry.trackingPlayers;
}
}

View File

@ -0,0 +1,83 @@
package mods.immibis.subworlds.dw;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import net.minecraft.world.ChunkPosition;
import net.minecraft.world.WorldType;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.biome.WorldChunkManager;
import net.minecraft.world.gen.layer.GenLayer;
@SuppressWarnings("rawtypes")
public class DWWorldChunkManager extends WorldChunkManager {
public final DWWorldProvider provider;
public DWWorldChunkManager(DWWorldProvider provider) {
this.provider = provider;
}
@Override
public boolean areBiomesViable(int par1, int par2, int par3, List par4List) {
return par4List.contains(DWChunkGenerator.BIOME);
}
@Override
public ChunkPosition findBiomePosition(int par1, int par2, int par3, List par4List, Random par5Random) {
if(par4List.contains(DWChunkGenerator.BIOME))
return new ChunkPosition(provider.props.xsize/2, provider.props.ysize/2, provider.props.zsize/2);
return null;
}
@Override
public BiomeGenBase[] getBiomeGenAt(BiomeGenBase[] ar, int par2, int par3, int par4, int par5, boolean par6) {
if (ar == null || ar.length < par4 * par5)
ar = new BiomeGenBase[par4 * par5];
Arrays.fill(ar, DWChunkGenerator.BIOME);
return ar;
}
@Override
public BiomeGenBase getBiomeGenAt(int par1, int par2) {
return DWChunkGenerator.BIOME;
}
@Override
public BiomeGenBase[] getBiomesForGeneration(BiomeGenBase[] ar, int par2, int par3, int par4, int par5) {
if (ar == null || ar.length < par4 * par5)
ar = new BiomeGenBase[par4 * par5];
Arrays.fill(ar, DWChunkGenerator.BIOME);
return ar;
}
@Override
public List getBiomesToSpawnIn() {
return Collections.singletonList(DWChunkGenerator.BIOME);
}
@Override
public GenLayer[] getModdedBiomeGenerators(WorldType worldType, long seed, GenLayer[] original) {
return original;
}
@Override
public float[] getRainfall(float[] ar, int par2, int par3, int par4, int par5) {
if(ar == null || ar.length < par4 * par5)
ar = new float[par4 * par5];
Arrays.fill(ar, DWChunkGenerator.BIOME.rainfall);
return ar;
}
@Override
public float getTemperatureAtHeight(float par1, int par2) {
return par1;
}
}

View File

@ -0,0 +1,226 @@
package mods.immibis.subworlds.dw;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.ChunkCoordinates;
import net.minecraft.util.Vec3;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.WorldServer;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class DWWorldProvider extends WorldProvider {
public WorldProps props;
public static final boolean
LIGHTNING = false,
RAIN = false,
RESPAWN_HERE = false,
FOG = false,
FULLBRIGHT = true;
public DWTeleporter teleporter;
@Override
public void setDimension(int dim) {
props = DWManager.getProps(dim);
super.setDimension(dim);
}
@Override
@SideOnly(Side.CLIENT)
public Vec3 getSkyColor(Entity cameraEntity, float partialTicks) {
return super.getSkyColor(cameraEntity, partialTicks);
}
@Override
public String getDimensionName() {
return "Detached World";
}
@Override
@SideOnly(Side.CLIENT)
public float[] calcSunriseSunsetColors(float par1, float par2) {
return null;
}
@Override
public boolean canCoordinateBeSpawn(int par1, int par2) {
return par1 >= 0 && par1 < props.xsize && par2 >= 0 && par2 < props.zsize;
}
@Override
public boolean canDoLightning(Chunk chunk) {
return LIGHTNING;
}
@Override
public boolean canDoRainSnowIce(Chunk chunk) {
return RAIN;
}
/*@Override
public boolean canMineBlock(EntityPlayer player, int x, int y, int z) {
return x >= 0 && y >= 0 && z >= 0 && x < HSIZE && y < VSIZE && z < HSIZE && super.canMineBlock(player, x, y, z);
}*/
@Override
public boolean canRespawnHere() {
return RESPAWN_HERE;
}
@Override
public IChunkProvider createChunkGenerator() {
try {
return props.generatorClass.getConstructor(WorldServer.class, DWWorldProvider.class).newInstance((WorldServer)worldObj, this);
} catch (Exception e) {
throw new RuntimeException(e);
}
//new DWChunkGenerator((WorldServer)worldObj, this);
}
@Override
@SideOnly(Side.CLIENT)
public boolean doesXZShowFog(int par1, int par2) {
return FOG;
}
@Override
protected void generateLightBrightnessTable() {
if(FULLBRIGHT) {
for(int k = 0; k < 16; k++)
lightBrightnessTable[k] = 1;
} else
super.generateLightBrightnessTable();
}
@Override
public int getActualHeight() {
return props.ysize;
}
@Override
public int getAverageGroundLevel() {
return 0;
}
@Override
public BiomeGenBase getBiomeGenForCoords(int x, int z) {
return DWChunkGenerator.BIOME;
}
@Override
@SideOnly(Side.CLIENT)
public Vec3 getFogColor(float par1, float par2) {
//return worldObj.getWorldVec3Pool().getVecFromPool((int)(par1*255), 255, (int)(par2*255));
return super.getFogColor(par1, par2);
}
@Override
public int getHeight() {
return props.ysize;
}
@Override
public ChunkCoordinates getRandomizedSpawnPoint() {
return new ChunkCoordinates(props.xsize/2, props.ysize/2, props.zsize/2);
}
@Override
public int getRespawnDimension(EntityPlayerMP player) {
return RESPAWN_HERE ? dimensionId : 0;
}
@Override
public String getSaveFolder() {
return "CRAFT" + dimensionId;
}
@Override
public long getSeed() {
return 0;
}
@Override
public ChunkCoordinates getSpawnPoint() {
return getRandomizedSpawnPoint();
}
@Override
@SideOnly(Side.CLIENT)
public double getVoidFogYFactor() {
return 1;
}
@Override
public String getWelcomeMessage() {
return "Entering sub-world...";
}
@Override
public String getDepartMessage() {
return "Leaving sub-world...";
}
@Override
@SideOnly(Side.CLIENT)
public boolean getWorldHasVoidParticles() {
return false;
}
@Override
public boolean isDaytime() {
return true;
}
@Override
public boolean isSurfaceWorld() {
return false;
}
@Override
protected void registerWorldChunkManager() {
worldChunkMgr = new DWWorldChunkManager(this);
if(!worldObj.isRemote)
teleporter = new DWTeleporter((WorldServer)worldObj);
}
@Override
public void setAllowedSpawnTypes(boolean allowHostile, boolean allowPeaceful) {
super.setAllowedSpawnTypes(false, false);
}
@Override
public void updateWeather() {
}
@Override
public boolean canSnowAt(int x, int y, int z, boolean checkLight) {
return false;
}
@Override
public boolean canBlockFreeze(int x, int y, int z, boolean byWater) {
return false;
}
@Override
public boolean canMineBlock(EntityPlayer player, int x, int y, int z) {
return super.canMineBlock(player, x, y, z);
}
@Override
public float calculateCelestialAngle(long par1, float par3) {
return super.calculateCelestialAngle(par1, par3);
}
@Override
public void calculateInitialWeather() {
}
}

View File

@ -0,0 +1,61 @@
package mods.immibis.subworlds.dw;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.core.api.net.PacketUtils;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.MinecraftServer;
public class PacketDWDimensionList implements IPacket {
public boolean clearExisting = false;
public Map<Integer, WorldProps> data = new HashMap<Integer, WorldProps>();
@Override
public byte getID() {
return DWManager.PKT_DIMENSION_LIST;
}
@Override
public String getChannel() {
return DWManager.CHANNEL;
}
@Override
public void read(DataInputStream in) throws IOException {
clearExisting = in.readBoolean();
int len = in.readInt();
for(int k = 0; k < len; k++) {
int id = in.readInt();
WorldProps wp = new WorldProps();
wp.read(PacketUtils.readNBT(in));
data.put(id, wp);
}
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeBoolean(clearExisting);
out.writeInt(data.size());
for(Map.Entry<Integer, WorldProps> e : data.entrySet()) {
out.writeInt(e.getKey());
NBTTagCompound tag = new NBTTagCompound();
e.getValue().write(tag);
PacketUtils.writeNBT(tag, out);
}
}
@Override
public void onReceived(EntityPlayer source) {
if(source == null && MinecraftServer.getServer() == null) {
DWManager.registerDimensions(data, clearExisting);
}
}
}

View File

@ -0,0 +1,32 @@
package mods.immibis.subworlds.dw;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.chunk.IChunkProvider;
// POD structure with extra info about a DW world
public final class WorldProps {
public int xsize = 48;
public int ysize = 48;
public int zsize = 48;
// class must have constructor taking (WorldServer, DWWorldProvider)
public Class<? extends IChunkProvider> generatorClass = DWChunkGenerator.class;
public void read(NBTTagCompound tag) {
xsize = tag.getInteger("xsize");
ysize = tag.getInteger("ysize");
zsize = tag.getInteger("zsize");
try {
generatorClass = Class.forName(tag.getString("genclass")).asSubclass(IChunkProvider.class);
} catch(ClassNotFoundException e) {
generatorClass = DWChunkGenerator.class;
}
}
public void write(NBTTagCompound tag) {
tag.setInteger("xsize", xsize);
tag.setInteger("ysize", ysize);
tag.setInteger("zsize", zsize);
tag.setString("genclass", generatorClass.getName());
}
}

View File

@ -0,0 +1,79 @@
package mods.immibis.subworlds.mws;
import mods.immibis.subworlds.dw.DWWorldProvider;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.RenderGlobal;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.WorldSettings;
import cpw.mods.fml.relauncher.ReflectionHelper;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class MWSClientWorld extends WorldClient {
public int dimension;
//private Chunk blankChunk;
public boolean isDead = false;
public MWSClientWorld(int dimID) {
// need a valid NetClientHandler for construction so grab one from the real world
super((NetHandlerPlayClient)ReflectionHelper.getPrivateValue(WorldClient.class, Minecraft.getMinecraft().theWorld, 0),
new WorldSettings(Minecraft.getMinecraft().theWorld.getWorldInfo()),
dimID,
EnumDifficulty.PEACEFUL, // difficulty
Minecraft.getMinecraft().theWorld.theProfiler);
// but now we don't need it any more
ReflectionHelper.setPrivateValue(WorldClient.class, this, null, 0);
dimension = dimID;
isRemote = true;
Minecraft mc = Minecraft.getMinecraft();
render = new RenderGlobal(mc); /*{
@Override
public void renderAllRenderLists(int par1, double par2) {
super.renderAllRenderLists(par1, par2);
System.out.println(par1+" "+par2+" "+((java.util.List)ReflectionHelper.getPrivateValue(RenderGlobal.class, this, "glRenderLists")).size());
}
};*/
final EntityRenderer normalER = mc.entityRenderer;
erender = new EntityRenderer(mc, mc.getResourceManager()) {
// use the normal lightmap, not our separate one
@Override public void disableLightmap(double par1) {normalER.disableLightmap(par1);}
@Override public void enableLightmap(double par1) {normalER.enableLightmap(par1);}
};
render.setWorldAndLoadRenderers(this);
//blankChunk = getChunkFromChunkCoords(0, 0);
if(provider instanceof DWWorldProvider) {
DWWorldProvider pv = (DWWorldProvider)provider;
for(int x = -1; x <= pv.props.xsize; x++)
for(int z = -1; z <= pv.props.zsize; z++)
doPreChunk(x, z, true);
}
}
public RenderGlobal render;
public EntityRenderer erender;
public void zeppelinTick() {
updateEntities();
}
public void unloadChunk(int x, int z) {
doPreChunk(x, z, false);
}
}

View File

@ -0,0 +1,34 @@
package mods.immibis.subworlds.mws;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.network.NetworkManager;
import net.minecraft.world.ChunkCoordIntPair;
/**
* Contains a reference to a client, as well as a
* mechanism for determining which part of the world to send to this client.
*/
public abstract class MWSListener {
public final NetworkManager client;
public MWSListener(NetworkManager client) {
this.client = client;
}
// See update()
public int x, y, z;
public boolean isDead;
public int ticksToNextRangeCheck = 0;
public int RANGE_CHECK_INTERVAL = 20;
Set<ChunkCoordIntPair> loadedChunks = new HashSet<ChunkCoordIntPair>();
/**
* Override this to update x, y and z with the centre of the area that will be sent to the client.
* Set isDead to true to remove the listener.
*/
public abstract void update();
}

View File

@ -0,0 +1,204 @@
package mods.immibis.subworlds.mws;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import mods.immibis.core.api.APILocator;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.core.api.net.IPacketMap;
import mods.immibis.core.api.net.IPacketWrapper;
import mods.immibis.subworlds.mws.packets.*;
import net.minecraft.entity.Entity;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.world.WorldEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.common.network.FMLNetworkEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/**
* MWS is Multi-World Sync. It handles sending worlds to clients that the player isn't in.
*
* Each server world is associated with an MWSWorldManager, even if no clients are watching it.
*/
public class MWSManager {
public static void debug(String s) {
System.out.println("[SubWorlds MWS Debug] "+s);
}
public static final String CHANNEL = "ImmMWS";
private static WeakHashMap<World, MWSWorldManager> managers = new WeakHashMap<World, MWSWorldManager>();
/**
* Returns the MWSWorldManager that controls MWS for a given server world.
*/
public static MWSWorldManager getWorldManager(World world) {
if(world.isRemote)
throw new IllegalArgumentException("Argument must be a server world.");
MWSWorldManager m = managers.get(world);
if(m == null)
managers.put(world, m = new MWSWorldManager(world));
return m;
}
@SideOnly(Side.CLIENT)
private static Map<Integer, MWSClientWorld> clientWorlds;
static {
try {
clientWorlds = new HashMap<Integer, MWSClientWorld>();
} catch(NoSuchFieldError e) {
}
}
/**
* Returns the MWSClientWorld for a given dimension ID, or null if none exists.
*/
@SideOnly(Side.CLIENT)
public static MWSClientWorld getClientWorld(int dimID) {
return clientWorlds.get(dimID);
}
@SideOnly(Side.CLIENT)
public static void clientBegin(int dimID) {
debug("MWS client start: "+dimID);
clientWorlds.put(dimID, new MWSClientWorld(dimID));
}
@SideOnly(Side.CLIENT)
public static void clientEnd(int dimID) {
debug("MWS client end: "+dimID);
MWSClientWorld w = clientWorlds.remove(dimID);
if(w != null) {
// is any cleanup necessary?
}
}
@SideOnly(Side.CLIENT)
public static Entity findClientEntity(int entID) {
for(MWSClientWorld w : clientWorlds.values()) {
Entity e = w.getEntityByID(entID);
if(e != null)
return e;
}
return null;
}
public static void init() {
MinecraftForge.EVENT_BUS.register(new ForgeEventListener());
APILocator.getNetManager().listen(new PacketMap());
FMLCommonHandler.instance().bus().register(new FMLEventListener());
}
// must be public or Forge throws an exception
public static class ForgeEventListener {
@SubscribeEvent
public void onWorldUnload(WorldEvent.Unload evt) {
if(!evt.world.isRemote) {
MWSWorldManager m = managers.get(evt.world);
if(m != null)
m.onWorldUnload();
}
}
}
public static final byte PKT_BEGIN = 0;
public static final byte PKT_BLOCK = 1;
public static final byte PKT_CHUNK = 2;
public static final byte PKT_MULTIBLOCK = 3;
public static final byte PKT_TILE = 4;
public static final byte PKT_UNLOAD = 5;
public static final byte PKT_END = 6;
public static final byte PKT_SET_WORLD = 7;
private static class PacketMap implements IPacketMap {
@Override
public String getChannel() {
return CHANNEL;
}
@Override
public IPacket createS2CPacket(byte id) {
switch(id) {
case PKT_BEGIN: return new PacketMWSBegin();
case PKT_BLOCK: return new PacketMWSBlock();
case PKT_CHUNK: return new PacketMWSChunk();
case PKT_MULTIBLOCK: return new PacketMWSMultiBlock();
case PKT_TILE: return new PacketMWSTile();
case PKT_UNLOAD: return new PacketMWSUnload();
case PKT_END: return new PacketMWSEnd();
case PKT_SET_WORLD: return new PacketMWSSetWorld();
default:
return null;
}
}
@Override
public IPacket createC2SPacket(byte id) {
return null;
}
}
// must be public or forge crashes
public static class FMLEventListener {
@SubscribeEvent
public void onServerTickEnd(TickEvent.ServerTickEvent evt) {
if(evt.phase != TickEvent.Phase.END) return;
for(MWSWorldManager m : managers.values())
m.tick();
}
@SubscribeEvent
@SideOnly(Side.CLIENT)
public void onClientSideConnect(FMLNetworkEvent.ClientConnectedToServerEvent evt) {
evt.manager.channel().pipeline().addBefore("packet_handler", "immibis subworlds mws subworld packet delayer congratulations if you are reading this far", new ChannelInboundHandlerAdapter() {
boolean delayingMessages = false;
int delayingMessagesForWorld;
List<Object> delayedMessages = new ArrayList<>();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if(msg instanceof IPacketWrapper) {
IPacket wp = ((IPacketWrapper)msg).packet;
if(wp instanceof PacketMWSSetWorld) {
int dim = ((PacketMWSSetWorld)wp).dim;
if(delayingMessages && dim != delayingMessagesForWorld) {
delayingMessages = false;
delayedMessages.add(new IPacketWrapper(new PacketMWSSetWorld(PacketMWSSetWorld.NORMAL_DIM)));
for(Object obj : delayedMessages)
ctx.fireChannelRead(obj);
delayedMessages.clear();
}
if(dim != PacketMWSSetWorld.NORMAL_DIM) {
delayingMessages = true;
delayingMessagesForWorld = dim;
delayedMessages.add(msg);
}
return;
}
}
if(delayingMessages)
delayedMessages.add(msg);
else
ctx.fireChannelRead(msg);
}
});
}
}
}

View File

@ -0,0 +1,457 @@
package mods.immibis.subworlds.mws;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import mods.immibis.core.api.APILocator;
import mods.immibis.subworlds.dw.DWWorldProvider;
import mods.immibis.subworlds.mws.packets.*;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.server.MinecraftServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.LongHashMap;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.IWorldAccess;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
/**
* Holds MWS state for one world.
*/
public class MWSWorldManager {
private final WeakReference<World> worldObj;
private final int dimensionID; // if world is unloaded, we need to know the dimension number to inform clients
private final int VIEW_DISTANCE = MinecraftServer.getServer().getConfigurationManager().getViewDistance();
MWSWorldManager(World world) {
this.worldObj = new WeakReference<World>(world);
this.dimensionID = world.provider.dimensionId;
world.addWorldAccess(new WorldListener());
}
private class Range {
// inclusive min, exclusive max
public final int minx, miny, minz, maxx, maxy, maxz;
public Range(int minx, int miny, int minz, int maxx, int maxy, int maxz) {
this.minx = minx;
this.miny = miny;
this.minz = minz;
this.maxx = maxx;
this.maxy = maxy;
this.maxz = maxz;
}
public int getBlockCount() {
return (maxx - minx) * (maxy - miny) * (maxz - minz);
}
}
private Set<MWSListener> listeners = new HashSet<MWSListener>();
private class SyncedChunk {
public final int x, z;
public SyncedChunk(int x, int z) {
this.x = x;
this.z = z;
}
private Range[] updateRanges = new Range[8];
private int nUpdateRanges;
private int nUpdatedBlocks;
//public Set<MWSListener> listeners = new HashSet<MWSListener>();
public void addRange(Range r) {
if(nUpdateRanges < updateRanges.length) {
updateRanges[nUpdateRanges] = r;
nUpdatedBlocks += r.getBlockCount();
}
nUpdateRanges++;
}
@SuppressWarnings("unchecked")
public Collection<Packet> nextUpdatePackets() {
if(nUpdateRanges == 0)
return null;
World world = worldObj.get();
if(world == null)
return null;
Collection<Packet> rv = null;
if(nUpdatedBlocks == 1) {
int x = updateRanges[0].minx + (this.x << 4);
int y = updateRanges[0].miny;
int z = updateRanges[0].minz + (this.z << 4);
updateRanges[0] = null;
Packet blockPacket = APILocator.getNetManager().wrap(new PacketMWSBlock(x, y, z, world), true);
TileEntity te = world.getTileEntity(x, y, z);
Packet tedesc = te == null ? null : te.getDescriptionPacket();
if(tedesc != null)
rv = Arrays.asList(APILocator.getNetManager().wrap(new PacketMWSTile(world, tedesc), true), blockPacket);
else
rv = Arrays.asList(blockPacket);
} else if(nUpdatedBlocks < 64 && nUpdateRanges <= updateRanges.length) {
PacketMWSMultiBlock mbp = new PacketMWSMultiBlock(this.x, this.z, nUpdatedBlocks, world);
rv = new ArrayList<Packet>(8);
rv.add(APILocator.getNetManager().wrap(mbp, true));
//Chunk c = world.getChunkFromChunkCoords(this.x, this.z);
for(int k = 0; k < nUpdateRanges; k++) {
Range r = updateRanges[k];
updateRanges[k] = null;
for(int x_ = r.minx; x_ < r.maxx; x_++)
for(int y = r.miny; y < r.maxy; y++)
for(int z_ = r.minz; z_ < r.maxz; z_++) {
int x = x_ + (this.x << 4);
int z = z_ + (this.z << 4);
mbp.addBlock(x, y, z, world);
TileEntity te = world.getTileEntity(x, y, z);
if(te != null) {
Packet teDesc = te.getDescriptionPacket();
if(teDesc != null)
rv.add(APILocator.getNetManager().wrap(new PacketMWSTile(world, teDesc), true));
}
}
}
} else {
Chunk c = world.getChunkFromChunkCoords(this.x, this.z);
rv = new ArrayList<Packet>(16);
rv.add(APILocator.getNetManager().wrap(new PacketMWSChunk(this.x, this.z, world, c), true));
for(TileEntity te : (Collection<TileEntity>)c.chunkTileEntityMap.values()) {
Packet teDesc = te.getDescriptionPacket();
if(teDesc != null)
rv.add(APILocator.getNetManager().wrap(new PacketMWSTile(world, teDesc), true));
}
}
nUpdateRanges = 0;
nUpdatedBlocks = 0;
return rv;
}
}
private Set<SyncedChunk> changedChunks = new HashSet<SyncedChunk>();
private Set<SyncedChunk> allChunks = new HashSet<SyncedChunk>();
private LongHashMap chunks = new LongHashMap();
private SyncedChunk getSyncedChunk(int x, int z, boolean markForUpdate) {
long index = ChunkCoordIntPair.chunkXZ2Int(x, z);
SyncedChunk s = (SyncedChunk)chunks.getValueByKey(index);
if(s == null) {
s = new SyncedChunk(x, z);
chunks.add(index, s);
allChunks.add(s);
}
if(markForUpdate)
synchronized(changedChunks) {
changedChunks.add(s);
}
return s;
}
private class WorldListener implements IWorldAccess {
@Override
public void markBlockForUpdate(int var1, int var2, int var3) {
getSyncedChunk(var1 >> 4, var3 >> 4, true).addRange(new Range(var1&15, var2, var3&15, var1&15, var2, var3&15));
}
@Override
public void markBlockForRenderUpdate(int var1, int var2, int var3) {}
@Override
public void markBlockRangeForRenderUpdate(int var1, int var2, int var3,int var4, int var5, int var6) {
int mincx = var1 >> 4;
int mincz = var3 >> 4;
int maxcx = var4 >> 4;
int maxcz = var6 >> 4;
for(int cx = mincx; cx <= maxcx; cx++) {
int minx = (cx == mincx ? var1 & 15 : 0);
int maxx = (cx == maxcx ? (var4 & 15) + 1 : 16);
for(int cz = mincz; cz <= maxcz; cz++) {
int minz = (cz == mincz ? var3 & 15 : 0);
int maxz = (cz == maxcz ? (var6 & 15) + 1 : 16);
getSyncedChunk(cx, cz, true).addRange(new Range(minx, var2, minz, maxx, var5, maxz));
}
}
}
@Override
public void playSound(String var1, double var2, double var4, double var6, float var8, float var9) {
}
@Override
public void spawnParticle(String var1, double var2, double var4, double var6, double var8, double var10, double var12) {
}
@Override
public void onEntityCreate(Entity var1) {
}
@Override
public void onEntityDestroy(Entity var1) {
}
@Override
public void playRecord(String var1, int var2, int var3, int var4) {
}
@Override
public void playAuxSFX(EntityPlayer var1, int var2, int var3, int var4, int var5, int var6) {
}
@Override
public void destroyBlockPartially(int var1, int var2, int var3, int var4, int var5) {
}
@Override
public void playSoundToNearExcept(EntityPlayer var1, String var2, double var3, double var5, double var7, float var9, float var10) {
}
@Override
public void broadcastSound(int var1, int var2, int var3, int var4, int var5) {
}
@Override
public void onStaticEntitiesChanged() {
// TODO Auto-generated method stub
}
}
/*private boolean isInViewingRange(SyncedChunk sc, MWSListener w) {
int minx = (sc.x - VIEW_DISTANCE) * 16;
int maxx = (sc.x + VIEW_DISTANCE + 1) * 16;
int minz = (sc.z - VIEW_DISTANCE) * 16;
int maxz = (sc.z + VIEW_DISTANCE + 1) * 16;
return w.x >= minx && w.z >= minz && w.x <= maxx && w.z <= maxz;
}*/
private void sendUpdate(SyncedChunk sc) {
Collection<Packet> packets = sc.nextUpdatePackets();
if(packets == null)
return;
int minx = (sc.x - VIEW_DISTANCE) * 16;
int maxx = (sc.x + VIEW_DISTANCE + 1) * 16;
int minz = (sc.z - VIEW_DISTANCE) * 16;
int maxz = (sc.z + VIEW_DISTANCE + 1) * 16;
for(MWSListener w : listeners) {
if(w.x >= minx && w.x <= maxx && w.z >= minz && w.z <= maxz) {
for(Packet p : packets) {
w.client.scheduleOutboundPacket(p);
}
}
}
}
public void tick() {
for(MWSListener l : new ArrayList<MWSListener>(listeners)) {
l.update();
if(l.isDead)
listeners.remove(l);
if(l.ticksToNextRangeCheck <= 0) {
l.ticksToNextRangeCheck = l.RANGE_CHECK_INTERVAL;
updateLoadedChunks(l);
} else
l.ticksToNextRangeCheck--;
}
synchronized(changedChunks) {
if(!changedChunks.isEmpty()) {
Collection<SyncedChunk> copy = new ArrayList<SyncedChunk>(changedChunks);
changedChunks.clear();
for(SyncedChunk sc : copy)
sendUpdate(sc);
}
}
final int MAX_CHUNKS_PER_TICK = 5;
int nSent = 0;
while(sendQueue.size() > 0) {
SendQueueEntry e = sendQueue.poll();
if(!listeners.contains(e.l))
continue;
//if(SubWorldsMod.sendQueueThrottle(e.l.client))
// break;
//System.out.println("sending "+e.hashCode()+": "+e.cx+","+e.cz+" to "+e.l);
e.send();
nSent++;
if(nSent >= MAX_CHUNKS_PER_TICK)
break;
}
}
private static class SendQueueEntry {
public MWSListener l;
public int cx, cz;
public World world;
public Chunk c;
public SendQueueEntry(MWSListener l, int cx, int cz, World world, Chunk c) {
this.c = c;
this.cx = cx;
this.cz = cz;
this.world = world;
this.l = l;
}
@SuppressWarnings("unchecked")
public void send() {
APILocator.getNetManager().send(new PacketMWSChunk(cx, cz, world, c), l.client, true);
//APILocator.getNetManager().send(new PacketMWSSetWorld(world.provider.dimensionId), l.client, true);
for(TileEntity te : (Collection<TileEntity>)c.chunkTileEntityMap.values()) {
Packet teDesc = te.getDescriptionPacket();
if(teDesc != null)
l.client.scheduleOutboundPacket(APILocator.getNetManager().wrap(new PacketMWSTile(world, teDesc), true));
//l.client.scheduleOutboundPacket(teDesc);
}
//APILocator.getNetManager().send(new PacketMWSSetWorld(PacketMWSSetWorld.NORMAL_DIM), l.client, true);
}
}
private Queue<SendQueueEntry> sendQueue = new LinkedList<SendQueueEntry>();
public void addListener(MWSListener l) {
World world = worldObj.get();
if(world == null)
return;
l.update();
if(l.isDead)
return;
if(!listeners.add(l))
return;
int minx, minz, maxx, maxz;
if(world.provider instanceof DWWorldProvider) {
minx = minz = 0;
maxx = ((DWWorldProvider)world.provider).props.xsize >> 4;
maxz = ((DWWorldProvider)world.provider).props.zsize >> 4;
} else {
minx = (l.x>>4) - VIEW_DISTANCE;
maxx = (l.x>>4) + VIEW_DISTANCE + 1;
minz = (l.z>>4) - VIEW_DISTANCE;
maxz = (l.z>>4) + VIEW_DISTANCE + 1;
}
l.client.scheduleOutboundPacket(APILocator.getNetManager().wrap(new PacketMWSBegin(world.provider.dimensionId), true));
for(int x = minx; x <= maxx; x++)
for(int z = minz; z <= maxz; z++) {
sendQueue.add(new SendQueueEntry(l, x, z, world, world.getChunkFromChunkCoords(x, z)));
}
}
private void updateLoadedChunks(MWSListener l) {
int minx, minz, maxx, maxz;
World world = worldObj.get();
if(world == null)
return;
if(world.provider instanceof DWWorldProvider) {
minx = minz = 0;
maxx = ((DWWorldProvider)world.provider).props.xsize >> 4;
maxz = ((DWWorldProvider)world.provider).props.zsize >> 4;
} else {
minx = (l.x>>4) - VIEW_DISTANCE;
maxx = (l.x>>4) + VIEW_DISTANCE + 1;
minz = (l.z>>4) - VIEW_DISTANCE;
maxz = (l.z>>4) + VIEW_DISTANCE + 1;
}
Set<ChunkCoordIntPair> targetChunks = new HashSet<ChunkCoordIntPair>();
for(int x = minx; x <= maxx; x++)
for(int z = minz; z <= maxz; z++) {
//System.out.println(l.x+" "+l.z+" "+x+" "+z);
targetChunks.add(new ChunkCoordIntPair(x, z));
}
Set<ChunkCoordIntPair> toLoad = new HashSet<ChunkCoordIntPair>(targetChunks);
Set<ChunkCoordIntPair> toUnload = new HashSet<ChunkCoordIntPair>(l.loadedChunks);
toLoad.removeAll(l.loadedChunks);
toUnload.removeAll(targetChunks);
/*System.out.println("target: "+targetChunks);
System.out.println("loaded: "+l.loadedChunks);
System.out.println(" load: "+toLoad);
System.out.println("unload: "+toUnload);*/
for(ChunkCoordIntPair ccip : toLoad) {
l.loadedChunks.add(ccip);
//System.out.println("load "+ccip);
sendQueue.add(new SendQueueEntry(l, ccip.chunkXPos, ccip.chunkZPos, world, world.getChunkFromChunkCoords(ccip.chunkXPos, ccip.chunkZPos)));
}
for(ChunkCoordIntPair ccip : toUnload) {
l.client.scheduleOutboundPacket(APILocator.getNetManager().wrap(new PacketMWSUnload(world, ccip.chunkXPos, ccip.chunkZPos), true));
//System.out.println("unload "+ccip);
l.loadedChunks.remove(ccip);
}
}
public void removeListener(NetworkManager client) {
for(MWSListener l : listeners) {
if(l.client == client) {
removeListener(l);
break;
}
}
}
public void removeListener(MWSListener l) {
l.isDead = true;
listeners.remove(l);
l.client.scheduleOutboundPacket(APILocator.getNetManager().wrap(new PacketMWSEnd(dimensionID), true));
}
void onWorldUnload() {
Packet p = APILocator.getNetManager().wrap(new PacketMWSEnd(dimensionID), true);
for(MWSListener l : listeners) {
l.isDead = true;
l.client.scheduleOutboundPacket(p);
}
listeners.clear();
worldObj.clear();
}
}

View File

@ -0,0 +1,53 @@
package mods.immibis.subworlds.mws.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.entity.player.EntityPlayer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PacketMWSBegin implements /*Runnable,*/ IPacket {
public int dim;
public PacketMWSBegin() {}
public PacketMWSBegin(int dim) {this.dim = dim;}
@Override
public byte getID() {
return MWSManager.PKT_BEGIN;
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(dim);
}
@Override
public void read(DataInputStream in) throws IOException {
dim = in.readInt();
}
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
/*MainThreadTaskQueue.enqueue(this, Side.CLIENT);
}
@Override
@SideOnly(Side.CLIENT)
public void run() {*/
MWSManager.clientBegin(dim);
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
}

View File

@ -0,0 +1,77 @@
package mods.immibis.subworlds.mws.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import mods.immibis.core.MainThreadTaskQueue;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PacketMWSBlock implements IPacket, Runnable {
int x, y, z, type, meta, dim;
public PacketMWSBlock(int x, int y, int z, World w) {
this.x = x;
this.y = y;
this.z = z;
this.dim = w.provider.dimensionId;
this.type = Block.getIdFromBlock(w.getBlock(x, y, z));
this.meta = w.getBlockMetadata(x, y, z);
}
public PacketMWSBlock() {}
@Override
public byte getID() {
return MWSManager.PKT_BLOCK;
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(x);
out.writeInt(y);
out.writeInt(z);
out.writeShort((short)type);
out.writeByte((byte)meta);
out.writeInt(dim);
}
@Override
public void read(DataInputStream in) throws IOException {
x = in.readInt();
y = in.readInt();
z = in.readInt();
type = in.readShort();
meta = in.readByte();
dim = in.readInt();
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
MainThreadTaskQueue.enqueue(this, Side.CLIENT);
}
@Override
@SideOnly(Side.CLIENT)
public void run() {
World w = MWSManager.getClientWorld(dim);
if(w != null) {
w.setBlock(x, y, z, Block.getBlockById(type), meta, 3);
}
}
}

View File

@ -0,0 +1,186 @@
package mods.immibis.subworlds.mws.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.block.Block;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PacketMWSChunk implements IPacket {
public int cx, cz, dim;
public short[] type;
public byte[] meta;
public byte[] light;
public short mask;
public PacketMWSChunk(int x, int z, World w, Chunk c) {
cx = x;
cz = z;
dim = w.provider.dimensionId;
fromChunk(c);
}
public PacketMWSChunk() {}
@Override
public byte getID() {
return MWSManager.PKT_CHUNK;
}
public void fromChunk(Chunk c) {
type = new short[16*16*256];
meta = new byte[16*16*256];
light = new byte[16*16*256];
mask = 0;
for(int k = 0; k < 16; k++)
if(c.getBlockStorageArray()[k] != null)
mask |= (1 << k);
int pos = 0;
for(int y = 0; y < 256; y++)
for(int x = 0; x < 16; x++)
for(int z = 0; z < 16; z++, pos++) {
type[pos] = (short)Block.getIdFromBlock(c.getBlock(x, y, z));
meta[pos] = (byte)c.getBlockMetadata(x, y, z);
byte L = (byte)(c.getSavedLightValue(EnumSkyBlock.Block, x, y, z) & 15);
byte SL = (byte)(c.getSavedLightValue(EnumSkyBlock.Sky, x, y, z) & 15);
light[pos] = (byte)((SL << 4) | L);
}
}
public void toChunk(Chunk c) {
ExtendedBlockStorage[] ebs = c.getBlockStorageArray();
int pos = 0;
for(int y = 0; y < 256; y++) {
ExtendedBlockStorage segment = ebs[y >> 4];
if((mask & (1 << (y >> 4))) == 0) {
pos += 256;
ebs[y >> 4] = null;
continue;
}
if(segment == null)
segment = ebs[y >> 4] = new ExtendedBlockStorage(y >> 4, true);
for(int x = 0; x < 16; x++)
for(int z = 0; z < 16; z++, pos++) {
int SL = (light[pos] >> 4) & 15;
int L = light[pos] & 15;
segment.setExtBlocklightValue(x, y&15, z, L);
segment.setExtSkylightValue(x, y&15, z, SL);
segment.func_150818_a(x, y&15, z, Block.getBlockById(type[pos]));
segment.setExtBlockMetadata(x, y&15, z, meta[pos]);
}
}
int x = c.xPosition << 4;
int z = c.zPosition << 4;
for(int k = 0; k < 16; k++)
if((mask & (1 << k)) != 0)
c.worldObj.markBlockRangeForRenderUpdate(x, k << 4, z, x+15, (k<<4)+15, z+15);
}
private static ThreadLocal<byte[]> buffer128kb = new ThreadLocal<byte[]>() {
@Override
protected byte[] initialValue() {return new byte[131072];}
};
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(cx);
out.writeInt(cz);
out.writeInt(dim);
out.writeShort(mask);
DeflaterOutputStream o = new DeflaterOutputStream(out, new Deflater());
//OutputStream o = out;
byte[] buffer = buffer128kb.get();
int pos = 0;
for(short s : type) {
buffer[pos++] = (byte)(s >> 8);
buffer[pos++] = (byte)s;
}
o.write(buffer);
o.write(meta);
o.write(light);
o.finish();
}
@Override
public void read(DataInputStream in) throws IOException {
cx = in.readInt();
cz = in.readInt();
dim = in.readInt();
mask = in.readShort();
InflaterInputStream i = new InflaterInputStream(in);
//InputStream i = in;
type = new short[65536];
meta = new byte[65536];
light = new byte[65536];
byte[] buffer = buffer128kb.get();
int pos = 0;
readFully(i, buffer);
for(int k = 0; k < 65536; k++, pos += 2) {
int b1 = buffer[pos] & 255;
int b2 = buffer[pos+1] & 255;
//if(b1 == -1 || b2 == -1)
// throw new IOException("unexpected EOF");
type[k] = (short)((b1 << 8) | b2);
}
readFully(i, meta);
readFully(i, light);
}
private void readFully(InputStream i, byte[] b) throws IOException {
int pos = 0;
while(pos < b.length) {
int read = i.read(b, pos, b.length - pos);
if(read < 0)
throw new IOException("Unexpected end of stream (after "+pos+" bytes, need "+b.length+")");
pos += read;
}
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
/*MainThreadTaskQueue.enqueue(this, Side.CLIENT);
}
@Override
@SideOnly(Side.CLIENT)
public void run() {*/
WorldClient w = MWSManager.getClientWorld(dim);
if(w != null) {
w.doPreChunk(cx, cz, true);
Chunk c = w.getChunkFromChunkCoords(cx, cz);
toChunk(c);
System.out.println("Received chunk "+dim+"/"+cx+"/"+cz);
}
}
}

View File

@ -0,0 +1,55 @@
package mods.immibis.subworlds.mws.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import mods.immibis.core.MainThreadTaskQueue;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.entity.player.EntityPlayer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PacketMWSEnd implements IPacket, Runnable {
public int dim;
public PacketMWSEnd() {}
public PacketMWSEnd(int dim) {this.dim = dim;}
@Override
public byte getID() {
return MWSManager.PKT_END;
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(dim);
}
@Override
public void read(DataInputStream in) throws IOException {
dim = in.readInt();
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
MainThreadTaskQueue.enqueue(this, Side.CLIENT);
}
@Override
@SideOnly(Side.CLIENT)
public void run() {
MWSManager.clientEnd(dim);
}
}

View File

@ -0,0 +1,107 @@
package mods.immibis.subworlds.mws.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import mods.immibis.core.MainThreadTaskQueue;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PacketMWSMultiBlock implements IPacket.Asynchronous, Runnable {
public PacketMWSMultiBlock() {}
public int size, pos, dim, cx, cz;
public byte[] meta;
public short[] type;
public byte[] x;
public int[] y;
public byte[] z;
public PacketMWSMultiBlock(int cx, int cz, int size, World w) {
this.size = size;
this.cx = cx;
this.cz = cz;
dim = w.provider.dimensionId;
pos = 0;
meta = new byte[size];
type = new short[size];
x = new byte[size];
y = new int[size];
z = new byte[size];
}
@Override
public byte getID() {
return MWSManager.PKT_MULTIBLOCK;
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(pos);
out.writeInt(cx);
out.writeInt(cz);
out.writeInt(dim);
for(int k = 0; k < pos; k++) {
out.writeByte(x[k]);
out.writeInt(y[k]);
out.writeByte(z[k]);
out.writeByte(meta[k]);
out.writeShort(type[k]);
}
}
@Override
public void read(DataInputStream in) throws IOException {
size = pos = in.readInt();
cx = in.readInt();
cz = in.readInt();
dim = in.readInt();
for(int k = 0; k < size; k++) {
x[k] = in.readByte();
y[k] = in.readInt();
z[k] = in.readByte();
meta[k] = in.readByte();
type[k] = in.readShort();
}
}
public void addBlock(int x2, int y2, int z2, World w) {
x[pos] = (byte)x2;
y[pos] = y2;
z[pos] = (byte)z2;
type[pos] = (short)Block.getIdFromBlock(w.getBlock(x2 + (cx << 4), y2, z2 + (cz << 4)));
meta[pos] = (byte)w.getBlockMetadata(x2 + (cx << 4), y2, z2 + (cz << 4));
pos++;
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
MainThreadTaskQueue.enqueue(this, Side.CLIENT);
}
@Override
@SideOnly(Side.CLIENT)
public void run() {
World w = MWSManager.getClientWorld(dim);
if(w != null) {
for(int k = 0; k < pos; k++) {
w.setBlock((cx<<4) + x[k], y[k], (cz<<4) + z[k], Block.getBlockById(type[k]), meta[k], 2);
}
}
}
}

View File

@ -0,0 +1,64 @@
package mods.immibis.subworlds.mws.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
public class PacketMWSSetWorld implements IPacket {
public static final int NORMAL_DIM = Integer.MIN_VALUE;
public int dim;
public PacketMWSSetWorld() {}
public PacketMWSSetWorld(int dim) {this.dim = dim;}
@Override
public byte getID() {
return MWSManager.PKT_SET_WORLD;
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(dim);
}
@Override
public void read(DataInputStream in) throws IOException {
dim = in.readInt();
}
@SideOnly(Side.CLIENT)
private static WorldClient oldWorld;
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
if(dim == NORMAL_DIM) {
if(oldWorld == null)
throw new AssertionError();
Minecraft.getMinecraft().theWorld = oldWorld;
oldWorld = null;
} else {
oldWorld = Minecraft.getMinecraft().theWorld;
WorldClient newWorld = MWSManager.getClientWorld(dim);
if(newWorld != null)
Minecraft.getMinecraft().theWorld = newWorld;
else
System.out.println("[WARNING] [SubWorlds MWS] Got set-world packet for unknown client world "+dim);
}
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
}

View File

@ -0,0 +1,124 @@
package mods.immibis.subworlds.mws.packets;
import io.netty.buffer.Unpooled;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.INetHandler;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PacketMWSTile implements IPacket {
public Packet teDesc;
public int dim;
public IOException exception;
public PacketMWSTile() {}
public PacketMWSTile(World w, Packet teDesc) {
this.teDesc = teDesc;
dim = w.provider.dimensionId;
}
@Override
public byte getID() {
return MWSManager.PKT_TILE;
}
@Override
public void write(DataOutputStream out) throws IOException {
if(exception != null)
throw new IOException("delayed wrapped encoding exception", exception);
Integer packetID = (Integer)EnumConnectionState.PLAY.func_150755_b().inverse().get(teDesc.getClass());
if(packetID == null) {
//System.out.println("can't send "+teDesc);
out.writeByte(0);
}
else
out.writeByte(packetID.intValue());
out.writeInt(dim);
if(packetID != null) {
PacketBuffer packetbuffer = new PacketBuffer(Unpooled.buffer());
teDesc.writePacketData(packetbuffer);
// TODO waste of memory/garbage
byte[] data = new byte[packetbuffer.readableBytes()];
packetbuffer.readBytes(data);
out.writeInt(data.length);
out.write(data);
}
}
@Override
public void read(DataInputStream in) throws IOException {
int packetID = in.readByte() & 255;
dim = in.readInt();
if(packetID == 0)
return;
int len = in.readInt();
if(len > 2097152)
throw new IOException("input packet size > 2MB");
// TODO waste of memory/garbage (considering this is coming from a ByteArrayInputStream)
byte[] data = new byte[len];
in.readFully(data);
Packet packet;
try {
packet = ((Class<? extends Packet>)EnumConnectionState.PLAY.func_150755_b().get(packetID)).getConstructor().newInstance();
} catch (ReflectiveOperationException e) {
throw new IOException("failed to instantiate packet", e);
}
packet.readPacketData(new PacketBuffer(Unpooled.wrappedBuffer(data)));
this.teDesc = packet;
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
if(teDesc == null)
return;
WorldClient w = MWSManager.getClientWorld(dim);
if(w != null) {
Minecraft mc = Minecraft.getMinecraft();
WorldClient oldWorld = mc.theWorld;
mc.theWorld = w;
try {
INetHandler handler = Minecraft.getMinecraft().getNetHandler();
System.out.println("Received tile packet "+teDesc);
teDesc.processPacket(handler);
} catch(Exception e) {
throw new RuntimeException(e);
} finally {
mc.theWorld = oldWorld;
}
}
}
}

View File

@ -0,0 +1,62 @@
package mods.immibis.subworlds.mws.packets;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import mods.immibis.core.MainThreadTaskQueue;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.client.multiplayer.WorldClient;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class PacketMWSUnload implements IPacket, Runnable {
public int dim, x, z;
public PacketMWSUnload() {}
public PacketMWSUnload(World world, int x, int z) {this.dim = world.provider.dimensionId; this.x = x; this.z = z;}
@Override
public byte getID() {
return MWSManager.PKT_UNLOAD;
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(dim);
out.writeInt(x);
out.writeInt(z);
}
@Override
public void read(DataInputStream in) throws IOException {
dim = in.readInt();
x = in.readInt();
z = in.readInt();
}
@Override
public String getChannel() {
return MWSManager.CHANNEL;
}
@Override
@SideOnly(Side.CLIENT)
public void onReceived(EntityPlayer source) {
MainThreadTaskQueue.enqueue(this, Side.CLIENT);
}
@Override
@SideOnly(Side.CLIENT)
public void run() {
WorldClient w = MWSManager.getClientWorld(dim);
if(w != null)
w.doPreChunk(x, z, false);
}
}

View File

@ -0,0 +1,85 @@
package mods.immibis.tinycarts;
import java.util.concurrent.atomic.AtomicInteger;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.world.World;
public class BlockTransparentBedrock extends Block {
public BlockTransparentBedrock() {
super(Material.rock);
setBlockUnbreakable();
setResistance(6000000.0F);
setStepSound(soundTypeStone);
setBlockName("tinycarts.transparent_bedrock");
disableStats();
setBlockTextureName("bedrock");
}
@Override
public boolean getUseNeighborBrightness() {
return true;
}
@Override
public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4) {
final float border = 1/16f;
return AxisAlignedBB.getBoundingBox((double)par2 + border, (double)par3 + this.minY, (double)par4 + border, (double)par2 + 1-border, (double)par3 + 1-border, (double)par4 + 1-border);
}
@Override
public void onEntityCollidedWithBlock(World par1World, int par2, int par3, int par4, Entity par5Entity) {
if(par5Entity instanceof EntityPlayerMP)
TinyCartsMod.removeFromCart(par5Entity, false);
}
// render in no passes = invisible
@Override
public boolean canRenderInPass(int pass) {
return false;
}
@Override
public boolean renderAsNormalBlock() {
return false;
}
@Override
public boolean isOpaqueCube() {
return false;
}
@Override
public void breakBlock(World par1World, int par2, int par3, int par4, Block par5, int par6) {
if(allowRemoval.get() > 0)
return;
allowPlacement.incrementAndGet();
try {
par1World.setBlock(par2, par3, par4, par5);
} finally {
allowPlacement.decrementAndGet();
}
}
@Override
public void onBlockAdded(World par1World, int par2, int par3, int par4) {
if(allowPlacement.get() > 0)
return;
allowRemoval.incrementAndGet();
try {
par1World.setBlockToAir(par2, par3, par4);
} finally {
allowRemoval.decrementAndGet();
}
}
static AtomicInteger allowPlacement = new AtomicInteger();
static AtomicInteger allowRemoval = new AtomicInteger();
}

View File

@ -0,0 +1,271 @@
package mods.immibis.tinycarts;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import mods.immibis.core.api.APILocator;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.subworlds.ClientFakeEntities;
import mods.immibis.subworlds.FakeEntity;
import mods.immibis.subworlds.dw.DWEntityRenderer;
import mods.immibis.subworlds.dw.DWWorldProvider;
import mods.immibis.subworlds.mws.MWSClientWorld;
import mods.immibis.subworlds.mws.MWSManager;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.network.Packet;
import net.minecraft.world.World;
import org.lwjgl.opengl.GL11;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class CartExternalFakeEntity extends FakeEntity {
public final int dimID;
// rotation is applied first when rendering, then translation.
// x/y/z is the coordinates of the centre of the player's world in the FE world (not vice versa)
public double x, y, z, yaw;
//public Quaternion rotation = new Quaternion();
private double prevX, prevY, prevZ, prevYaw;
//private Quaternion prevRotation = rotation.clone();
public CartExternalFakeEntity(boolean isClient, int dimID) {
super(isClient);
this.dimID = dimID;
}
public CartExternalFakeEntity(boolean isClient, int entID, int dimID) {
super(isClient, entID);
this.dimID = dimID;
}
@Override
public Packet getUpdatePacket() {
final double MOVE_THRESHOLD = 0.2;
if(prevYaw == yaw && Math.abs(x - prevX) < MOVE_THRESHOLD && Math.abs(y - prevY) < MOVE_THRESHOLD && Math.abs(z - prevZ) < MOVE_THRESHOLD) {
return null;
}
prevYaw = yaw;
prevX = x;
prevY = y;
prevZ = z;
return APILocator.getNetManager().wrap(new UpdatePacket(this), true);
}
@Override
public Packet getDescriptionPacket() {
return APILocator.getNetManager().wrap(new CreatePacket(this), true);
}
@Override
public Packet getDestructionPacket() {
return APILocator.getNetManager().wrap(new DeletePacket(entityID), true);
}
private boolean updated = false;
@Override
public void tick() {
super.tick();
if(isClient) {
if(updated)
updated = false;
else {
prevX = x;
prevY = y;
prevZ = z;
prevYaw = yaw;
}
}
}
@Override
@SideOnly(Side.CLIENT)
public void render(double pl_x, double pl_y, double pl_z, float partialTick) {
MWSClientWorld subWorld = MWSManager.getClientWorld(dimID);
World plWorld = Minecraft.getMinecraft().theWorld;
if(subWorld == null || !(plWorld.provider instanceof DWWorldProvider)) {
super.render(pl_x, pl_y, pl_z, partialTick);
return;
}
GL11.glPushMatrix();
GL11.glTranslated(-pl_x, -pl_y, -pl_z);
// now 0,0,0 = origin of the world the player is in
DWWorldProvider pv = (DWWorldProvider)plWorld.provider;
GL11.glTranslated(pv.props.xsize/2, 0, pv.props.zsize/2);
// now 0,0,0 = centre-bottom of world the player is in
double cx = prevX + (x - prevX) * partialTick;
double cy = prevY + (y - prevY) * partialTick;
double cz = prevZ + (z - prevZ) * partialTick;
// why not just cx,cy,cz?
GL11.glTranslated(cx-x, cy-y, cz-z);
GL11.glScalef(EntityMinecartAwesome.SCALE, EntityMinecartAwesome.SCALE, EntityMinecartAwesome.SCALE);
GL11.glRotatef((float)yaw, 0, 1, 0);
DWEntityRenderer.renderClientWorld(subWorld, partialTick, 0, 0, 0, x, y, z);
GL11.glPopMatrix();
OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GL11.glDisable(GL11.GL_TEXTURE_2D);
OpenGlHelper.setActiveTexture(OpenGlHelper.defaultTexUnit);
RenderHelper.disableStandardItemLighting();
GL11.glColor3f(1, 1, 1);
}
public static class DeletePacket implements IPacket {
@Override
public String getChannel() {
return NetworkHandler.CHANNEL;
}
@Override
public byte getID() {
return NetworkHandler.PKT_CEFE_DELETE;
}
public int entID;
public DeletePacket() {}
public DeletePacket(int id) {entID = id;}
@Override
public void onReceived(EntityPlayer source) {
ClientFakeEntities.remove(entID);
}
@Override
public void read(DataInputStream in) throws IOException {
entID = in.readInt();
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(entID);
}
}
public static class CreatePacket implements IPacket {
@Override
public String getChannel() {
return NetworkHandler.CHANNEL;
}
@Override
public byte getID() {
return NetworkHandler.PKT_CEFE_CREATE;
}
public int entID, dimID;
public double x, y, z, r;
public CreatePacket() {}
public CreatePacket(CartExternalFakeEntity ent) {
entID = ent.entityID;
dimID = ent.dimID;
x = ent.x;
y = ent.y;
z = ent.z;
r = ent.yaw;
}
@Override
public void onReceived(EntityPlayer source) {
CartExternalFakeEntity e = new CartExternalFakeEntity(true, entID, dimID);
e.x = x;
e.y = y;
e.z = z;
e.yaw = r;
ClientFakeEntities.add(e);
}
@Override
public void read(DataInputStream in) throws IOException {
entID = in.readInt();
x = in.readDouble();
y = in.readDouble();
z = in.readDouble();
r = in.readDouble();
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(entID);
out.writeDouble(x);
out.writeDouble(y);
out.writeDouble(z);
out.writeDouble(r);
}
}
public static class UpdatePacket implements IPacket {
@Override
public String getChannel() {
return NetworkHandler.CHANNEL;
}
@Override
public byte getID() {
return NetworkHandler.PKT_CEFE_UPDATE;
}
public int entID;
public double x, y, z;
public float rot;
public UpdatePacket() {}
public UpdatePacket(CartExternalFakeEntity ent) {
entID = ent.entityID;
x = ent.x;
y = ent.y;
z = ent.z;
rot = (float)ent.yaw;
}
@Override
public void onReceived(EntityPlayer source) {
CartExternalFakeEntity ent = (CartExternalFakeEntity)ClientFakeEntities.get(entID);
ent.prevX = x;
ent.prevY = y;
ent.prevZ = z;
ent.updated = true;
ent.x = x;
ent.y = y;
ent.z = z;
ent.yaw = rot;
}
@Override
public void read(DataInputStream in) throws IOException {
entID = in.readInt();
x = in.readDouble();
y = in.readDouble();
z = in.readDouble();
rot = in.readFloat();
}
@Override
public void write(DataOutputStream out) throws IOException {
out.writeInt(entID);
out.writeDouble(x);
out.writeDouble(y);
out.writeDouble(z);
out.writeFloat(rot);
}
}
}

View File

@ -0,0 +1,31 @@
package mods.immibis.tinycarts;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ChatComponentText;
public class CommandExitCart extends CommandBase {
@Override
public boolean canCommandSenderUseCommand(ICommandSender par1iCommandSender) {
return par1iCommandSender instanceof EntityPlayer;
}
@Override
public String getCommandName() {
return "exitcart";
}
@Override
public String getCommandUsage(ICommandSender icommandsender) {
return "/exitcart";
}
@Override
public void processCommand(ICommandSender icommandsender, String[] astring) {
if(icommandsender instanceof EntityPlayer)
TinyCartsMod.removeFromCart((EntityPlayer)icommandsender, true);
else
icommandsender.addChatMessage(new ChatComponentText("That command is only applicable to players."));
}
}

View File

@ -0,0 +1,340 @@
package mods.immibis.tinycarts;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import mods.immibis.subworlds.FakeEntity;
import mods.immibis.subworlds.SubWorldsMod;
import mods.immibis.subworlds.dw.DWEntity;
import mods.immibis.subworlds.dw.DWManager;
import mods.immibis.subworlds.dw.DWWorldProvider;
import mods.immibis.subworlds.dw.WorldProps;
import net.minecraft.entity.item.EntityMinecart;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.ServerConfigurationManager;
import net.minecraft.util.Vec3;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.ForgeChunkManager;
import net.minecraftforge.common.ForgeChunkManager.Ticket;
import net.minecraftforge.common.ForgeChunkManager.Type;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.minecart.MinecartInteractEvent;
import org.lwjgl.opengl.GL11;
public class EntityMinecartAwesome extends EntityMinecart {
public static final int XSIZE = 16;
public static final int ZSIZE = 10;
public static final int YSIZE = 10;
public static final int SCALE = 10; // render scale; internal blocks per external block
public EntityMinecartAwesome(World par1World) {
super(par1World);
}
public EntityMinecartAwesome(World w, double x, double y, double z) {
super(w, x, y, z);
if(w.isRemote)
throw new IllegalStateException();
WorldProps props = new WorldProps();
props.xsize = XSIZE;
props.ysize = YSIZE;
props.zsize = ZSIZE;
props.generatorClass = InteriorChunkGen.class;
internalWorldID = DWManager.createWorld(props);
}
@Override
public double getMountedYOffset() {
return 0.3f;
}
private int internalWorldID;
@Override
protected void readEntityFromNBT(NBTTagCompound par1nbtTagCompound) {
super.readEntityFromNBT(par1nbtTagCompound);
internalWorldID = par1nbtTagCompound.getInteger("intwid");
}
@Override
protected void writeEntityToNBT(NBTTagCompound par1nbtTagCompound) {
super.writeEntityToNBT(par1nbtTagCompound);
par1nbtTagCompound.setInteger("intwid", internalWorldID);
}
private DWSubEntity getDW() {
if(riddenByEntity instanceof DWSubEntity)
return (DWSubEntity)riddenByEntity;
else if(worldObj.isRemote)
return null;
else {
DWSubEntity dwsub = new DWSubEntity(worldObj);
dwsub.posX = posX;
dwsub.posY = posY;
dwsub.posZ = posZ;
worldObj.spawnEntityInWorld(dwsub);
dwsub.mountEntity(this);
return dwsub;
}
}
@Override
public void onUpdate() {
super.onUpdate();
getDW();
}
@Override
public boolean interactFirst(EntityPlayer par1EntityPlayer)
{
if(MinecraftForge.EVENT_BUS.post(new MinecartInteractEvent(this, par1EntityPlayer)))
return true;
if(!(par1EntityPlayer instanceof EntityPlayerMP))
return false;
if(worldObj.isRemote)
return true;
ServerConfigurationManager scm = MinecraftServer.getServer().getConfigurationManager();
scm.transferPlayerToDimension((EntityPlayerMP)par1EntityPlayer, internalWorldID, ((DWWorldProvider)getDW().getInternalWorld().provider).teleporter);
return true;
}
@Override
public int getMinecartType() {
return -1;
}
// XXX the whole purpose of this field is hacky. subworlds should keep track of this for us.
static Map<Integer, WeakReference<DWSubEntity>> entitiesByInternalID = new HashMap<Integer, WeakReference<DWSubEntity>>();
public static class DWSubEntity extends DWEntity {
public DWSubEntity(World w) {
super(w);
setSize(Math.min(XSIZE, ZSIZE)/(float)SCALE, YSIZE/(float)SCALE);
}
public EntityMinecartAwesome getCart() {
if(ridingEntity instanceof EntityMinecartAwesome)
return (EntityMinecartAwesome)ridingEntity;
return null;
}
private int internalWorldID = Integer.MIN_VALUE;
@Override
public void onUnloadOrDestroy() {
if(!worldObj.isRemote) {
synchronized(entitiesByInternalID) {
int intWID = internalWorldID;
if(intWID != Integer.MIN_VALUE) {
WeakReference<DWSubEntity> ent = entitiesByInternalID.get(intWID);
if(ent != null && ent.get() == this) {
entitiesByInternalID.remove(intWID);
}
}
}
}
mountEntity(null);
super.onUnloadOrDestroy();
}
private Ticket externalTicket, internalTicket;
private static final int EXTERNAL_VIEW_DISTANCE = 7;
private static final boolean DEBUG = false;
private void updateChunkLoading() {
boolean load = requiresInteriorLoaded();
if(load != (externalTicket != null)) {
if(DEBUG) System.out.println("[TinyCarts DEBUG] External loading: now "+load);
if(load) {
WorldServer world = (WorldServer)worldObj;
externalTicket = ForgeChunkManager.requestTicket(SubWorldsMod.INSTANCE, world, Type.NORMAL);
int baseX = (int)(posX) >> 4;
int baseZ = (int)(posZ) >> 4;
for(int dx = -EXTERNAL_VIEW_DISTANCE; dx <= EXTERNAL_VIEW_DISTANCE; dx++)
for(int dz = -EXTERNAL_VIEW_DISTANCE; dz <= EXTERNAL_VIEW_DISTANCE; dz++) {
int x = dx + baseX, z = dz + baseZ;
ForgeChunkManager.forceChunk(externalTicket, new ChunkCoordIntPair(x, z));
world.theChunkProviderServer.loadChunk(x, z);
}
} else {
ForgeChunkManager.releaseTicket(externalTicket);
externalTicket = null;
}
}
if(load != (internalTicket != null)) {
if(load) {
WorldServer world = getInternalWorld();
if(world == null)
throw new IllegalStateException("Failed to load internal world.");
internalTicket = ForgeChunkManager.requestTicket(SubWorldsMod.INSTANCE, world, Type.NORMAL);
int size = (Math.max(XSIZE, YSIZE)+15)/16;
if(DEBUG) System.out.println("[TinyCarts DEBUG] Internal loading: now true ("+size+"x"+size+")");
for(int x = 0; x < size; x++)
for(int z = 0; z < size; z++) {
ForgeChunkManager.forceChunk(internalTicket, new ChunkCoordIntPair(x, z));
world.theChunkProviderServer.loadChunk(x, z);
}
} else {
if(DEBUG) System.out.println("[TinyCarts DEBUG] Internal loading: now false");
ForgeChunkManager.releaseTicket(internalTicket);
internalTicket = null;
}
}
if(!isDead) { // should always be true
int intWID = internalWorldID = getCart().internalWorldID;
synchronized(entitiesByInternalID) {
WeakReference<DWSubEntity> ref = entitiesByInternalID.get(intWID);
DWSubEntity reffed = (ref == null ? null : ref.get());
if(reffed == null || (reffed.isDead && reffed != this)) {
entitiesByInternalID.put(intWID, new WeakReference<DWSubEntity>(this));
}
}
} else assert false : "updateChunkLoading on dead entity?";
}
private static float getActualClientMinecartYaw(EntityMinecart cart, double old) {
double x = cart.posX, y = cart.posY, z = cart.posZ;
Vec3 vec3 = cart.func_70489_a(x, y, z);
//float f5 = cart.rotationPitch;
double d6 = 0.30000001192092896D;
float yaw = cart.rotationYaw;
if (vec3 != null)
{
Vec3 vec31 = cart.func_70495_a(x, y, z, d6);
Vec3 vec32 = cart.func_70495_a(x, y, z, -d6);
if (vec31 == null)
{
vec31 = vec3;
}
if (vec32 == null)
{
vec32 = vec3;
}
//renderX += vec3.xCoord - x;
//renderY += (vec31.yCoord + vec32.yCoord) / 2.0D - y;
//renderZ += vec3.zCoord - z;
Vec3 vec33 = vec32.addVector(-vec31.xCoord, -vec31.yCoord, -vec31.zCoord);
if (vec33.lengthVector() != 0.0D)
{
vec33 = vec33.normalize();
yaw = (float)(Math.atan2(vec33.zCoord, vec33.xCoord) * 180.0D / Math.PI);
//f5 = (float)(Math.atan(vec33.yCoord) * 73.0D);
}
}
/*if(Math.abs(angleDiff(old, yaw)) > Math.abs(angleDiff(old, yaw+180)))
if(yaw > 0)
yaw -= 180;
else
yaw += 180;*/
return yaw;
}
/*private static double angleDiff(double a, double b) {
b -= a;
if(b < 0)
b = (b % 360) + 360;
b = (b + 180) % 360 - 180;
assert !(b < -180 || b > 180) : "angleDiff is broken";
return b;
}*/
@Override
public void onUpdate() {
if(!worldObj.isRemote && (getCart() == null || getCart().getDW() != this)) {
setDead();
return;
}
//if(ridingEntity != null && worldObj.isRemote)
// System.out.println(worldObj.isRemote+" "+(int)posX+" "+(int)posY+" "+(int)posZ+" "+(int)ridingEntity.posX+" "+(int)ridingEntity.posY+" "+(int)ridingEntity.posZ);
if(getCart() != null)
rotationYaw = !worldObj.isRemote ? ridingEntity.rotationYaw : getActualClientMinecartYaw(getCart(), rotationYaw);
//if(worldObj.isRemote) {
//System.out.println("yaw "+rotationYaw);
//}
super.onUpdate();
if(!worldObj.isRemote) {
updateChunkLoading();
}
}
@Override
protected void applyGLRotation(float partialTick) {
GL11.glScalef(1.0f/SCALE, 1.0f/SCALE, 1.0f/SCALE);
if(ridingEntity != null)
GL11.glRotatef(-rotationYaw, 0, 1, 0);
}
@Override
protected FakeEntity createExternalWorldFE() {
return new CartExternalFakeEntity(false, getCart().internalWorldID);
}
@Override
protected WorldServer getInternalWorld() {
return MinecraftServer.getServer().worldServerForDimension(getCart().internalWorldID);
}
@Override
protected void updateExternalWorldFE(FakeEntity e_) {
CartExternalFakeEntity e = (CartExternalFakeEntity)e_;
e.x = posX;
e.y = posY;
e.z = posZ;
e.yaw = ridingEntity == null ? 0 : ridingEntity.rotationYaw;
}
// this is called when a position update is received from the server.
// by overriding it we make sure the client doesn't try to be smart and push this entity out of bounding boxes.
// also, ignore the position from the update packet. because it's incorrect for entities that are riding other entities.
// also, ignore the rotation. because we copy that from the cart entity.
// in fact, just do nothing when a position/rotation update is received.
@Override
public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9) {
//this.setPosition(par1, par3, par5);
//this.setRotation(par7, par8);
}
}
}

View File

@ -0,0 +1,120 @@
package mods.immibis.tinycarts;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import mods.immibis.subworlds.dw.DWWorldProvider;
import mods.immibis.subworlds.dw.WorldProps;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.init.Blocks;
import net.minecraft.util.IProgressUpdate;
import net.minecraft.world.ChunkPosition;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
public class InteriorChunkGen implements IChunkProvider {
private WorldProps props;
private WorldServer w;
public InteriorChunkGen(WorldServer w, DWWorldProvider wp) {
this.props = wp.props;
this.w = w;
}
@Override
public Chunk provideChunk(int i, int j) {
Chunk c = new Chunk(w, i, j);
BlockTransparentBedrock.allowPlacement.incrementAndGet();
try {
if(i == 0 && j == 0) {
for(int x = 0; x < props.xsize; x++)
for(int z = 0; z < props.zsize; z++) {
c.func_150807_a(x, 0, z, TinyCartsMod.transparentBedrock, 0);
c.func_150807_a(x, props.ysize-1, z, TinyCartsMod.transparentBedrock, 0);
c.func_150807_a(x, 1, z, Blocks.planks, 0);
}
for(int x = 0; x < props.xsize; x++)
for(int y = 0; y < props.ysize; y++) {
c.func_150807_a(x, y, 0, TinyCartsMod.transparentBedrock, 0);
c.func_150807_a(x, y, props.zsize-1, TinyCartsMod.transparentBedrock, 0);
c.func_150807_a(0, y, x, TinyCartsMod.transparentBedrock, 0);
c.func_150807_a(props.xsize-1, y, x, TinyCartsMod.transparentBedrock, 0);
}
}
} finally {
BlockTransparentBedrock.allowPlacement.decrementAndGet();
}
c.generateSkylightMap();
Arrays.fill(c.getBiomeArray(), (byte)BiomeGenBase.plains.biomeID);
c.isTerrainPopulated = true;
return c;
}
@Override
public boolean chunkExists(int i, int j) {
return true;
}
@Override
public Chunk loadChunk(int i, int j) {
return provideChunk(i, j);
}
@Override
public void populate(IChunkProvider ichunkprovider, int i, int j) {
}
@Override
public boolean saveChunks(boolean flag, IProgressUpdate iprogressupdate) {
return true;
}
@Override
public boolean unloadQueuedChunks() {
return false;
}
@Override
public boolean canSave() {
return true;
}
@Override
public String makeString() {
return getClass().getName();
}
@Override
public List<?> getPossibleCreatures(EnumCreatureType enumcreaturetype, int i, int j, int k) {
return Collections.EMPTY_LIST;
}
@Override
public ChunkPosition func_147416_a(World var1, String var2, int var3, int var4, int var5) {
return null;
}
@Override
public int getLoadedChunkCount() {
return 0;
}
@Override
public void recreateStructures(int i, int j) {
}
@Override
public void saveExtraData() {
}
}

View File

@ -0,0 +1,57 @@
package mods.immibis.tinycarts;
import mods.immibis.subworlds.dw.DWWorldProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockRailBase;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.item.EntityMinecart;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ChatComponentText;
import net.minecraft.world.World;
public class ItemMinecartAwesome extends Item {
public ItemMinecartAwesome()
{
this.maxStackSize = 1;
this.setCreativeTab(CreativeTabs.tabTransport);
this.setTextureName("tinycarts:minecart");
this.setUnlocalizedName("tinycarts.cart");
}
@Override
public boolean onItemUse(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, World par3World, int par4, int par5, int par6, int par7, float par8, float par9, float par10)
{
if(par3World.provider instanceof DWWorldProvider) {
if(par3World.isRemote)
par2EntityPlayer.addChatMessage(new ChatComponentText("Don't even think about it."));
return false;
}
Block i1 = par3World.getBlock(par4, par5, par6);
if (BlockRailBase.func_150051_a(i1))
{
if (!par3World.isRemote)
{
EntityMinecart entityminecart = new EntityMinecartAwesome(par3World, (double)((float)par4 + 0.5F), (double)((float)par5 + 0.5F), (double)((float)par6 + 0.5F));
if (par1ItemStack.hasDisplayName())
{
entityminecart.setMinecartName(par1ItemStack.getDisplayName());
}
par3World.spawnEntityInWorld(entityminecart);
}
--par1ItemStack.stackSize;
return true;
}
else
{
return false;
}
}
}

View File

@ -0,0 +1,34 @@
package mods.immibis.tinycarts;
import mods.immibis.core.api.net.IPacket;
import mods.immibis.core.api.net.IPacketMap;
public class NetworkHandler implements IPacketMap {
public static final String CHANNEL = "TinyCarts";
public static final byte PKT_CEFE_CREATE = 0;
public static final byte PKT_CEFE_DELETE = 1;
public static final byte PKT_CEFE_UPDATE = 2;
@Override
public String getChannel() {
return CHANNEL;
}
@Override
public IPacket createS2CPacket(byte id) {
switch(id) {
case PKT_CEFE_CREATE: return new CartExternalFakeEntity.CreatePacket();
case PKT_CEFE_DELETE: return new CartExternalFakeEntity.DeletePacket();
case PKT_CEFE_UPDATE: return new CartExternalFakeEntity.UpdatePacket();
}
return null;
}
@Override
public IPacket createC2SPacket(byte id) {
return null;
}
}

View File

@ -0,0 +1,14 @@
package mods.immibis.tinycarts;
import net.minecraft.client.renderer.entity.RenderMinecart;
import net.minecraft.entity.item.EntityMinecart;
import net.minecraft.util.ResourceLocation;
public class RenderMinecartAwesome extends RenderMinecart {
private static final ResourceLocation texture = new ResourceLocation("tinycarts", "textures/entity/minecart.png");
@Override
protected ResourceLocation getEntityTexture(EntityMinecart par1EntityMinecart) {
return texture;
}
}

View File

@ -0,0 +1,122 @@
package mods.immibis.tinycarts;
import java.lang.ref.WeakReference;
import mods.immibis.cobaltite.AssignedBlock;
import mods.immibis.cobaltite.AssignedItem;
import mods.immibis.cobaltite.CobaltiteMod;
import mods.immibis.cobaltite.ModBase;
import mods.immibis.core.api.APILocator;
import mods.immibis.core.api.FMLModInfo;
import mods.immibis.subworlds.ExitTeleporter;
import mods.immibis.subworlds.dw.DWWorldProvider;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.management.ServerConfigurationManager;
import net.minecraft.util.ChatComponentText;
import net.minecraft.world.WorldServer;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
/**
* IMPORTANT SMALL DETAIL: -X EITHER FORWARD OR BACKWARD, -Z IS SIDEWAYS.
*/
@Mod(modid="TinyCarts", name="TinyCarts", version="0.2", dependencies="required-after:SubWorlds")
@CobaltiteMod()
@FMLModInfo(authors="immibis", description="", url="")
public class TinyCartsMod extends ModBase {
@AssignedItem(id="awesomecart")
public static ItemMinecartAwesome itemCart;
@AssignedBlock(id="transparentbedrock")
public static BlockTransparentBedrock transparentBedrock;
@EventHandler
public void init(FMLInitializationEvent evt) {
EntityRegistry.registerModEntity(EntityMinecartAwesome.class, "tinycarts.cart", 0, this, 50, 1, true);
EntityRegistry.registerModEntity(EntityMinecartAwesome.DWSubEntity.class, "tinycarts.cart.dwsub", 1, this, 50, 1, true);
APILocator.getNetManager().listen(new NetworkHandler());
}
@Override
protected void addRecipes() throws Exception {
GameRegistry.addRecipe(new ItemStack(itemCart), "D D", "DDD", 'D', Items.diamond);
}
@EventHandler
public void onServerStart(FMLServerStartingEvent evt) {
evt.registerServerCommand(new CommandExitCart());
}
@Override
@SideOnly(Side.CLIENT)
protected void clientInit() throws Exception {
RenderingRegistry.registerEntityRenderingHandler(EntityMinecartAwesome.class, new RenderMinecartAwesome());
}
@EventHandler
public void __init(FMLInitializationEvent evt) {super._init(evt);}
@EventHandler
public void __preinit(FMLPreInitializationEvent evt) {super._preinit(evt);}
private static long lastRFCWarnTime = 0;
public static void removeFromCart(Entity ent, boolean ignoreIfNotInCart) {
if(ent.worldObj.isRemote) {
assert false : "removeFromCart - on client world!";
return;
}
if(!(ent.worldObj.provider instanceof DWWorldProvider)) {
assert ignoreIfNotInCart : "removeFromCart - not in DW world!";
return;
}
DWWorldProvider wp = (DWWorldProvider)ent.worldObj.provider;
if(wp.props.generatorClass != InteriorChunkGen.class) {
assert ignoreIfNotInCart : "removeFromCart - not in TinyCart world!";
return;
}
// XXX this way of getting the entity is hacky
WeakReference<EntityMinecartAwesome.DWSubEntity> dwsub_ref = EntityMinecartAwesome.entitiesByInternalID.get(wp.dimensionId);
EntityMinecartAwesome.DWSubEntity dwsub = dwsub_ref == null ? null : dwsub_ref.get();
if(dwsub == null) {
if(lastRFCWarnTime - System.nanoTime() > 5000) {
System.out.println("[TinyCarts] Potential problem - entity is leaving a cart, but we can't find the outside of the cart! Entity is "+ent+", internal world ID is "+ent.worldObj.provider.dimensionId+". This message will only be printed once every 5 seconds, maximum.");
lastRFCWarnTime = System.nanoTime();
}
if(ent instanceof EntityPlayer)
((EntityPlayer)ent).addChatMessage(new ChatComponentText("Uh oh! Outside of the cart could not be found! Cannot teleport you, sorry."));
return;
}
WorldServer outsideWorld = (WorldServer)dwsub.worldObj;
double x = dwsub.posX;
double y = dwsub.posY;
double z = dwsub.posZ;
x += (outsideWorld.rand.nextDouble() + 1) * (outsideWorld.rand.nextBoolean() ? -1 : 1);
z += (outsideWorld.rand.nextDouble() + 1) * (outsideWorld.rand.nextBoolean() ? -1 : 1);
ServerConfigurationManager scm = MinecraftServer.getServer().getConfigurationManager();
if(ent instanceof EntityPlayerMP)
scm.transferPlayerToDimension((EntityPlayerMP)ent, outsideWorld.provider.dimensionId, new ExitTeleporter(outsideWorld, x, y, z));
//else
// scm.transferEntityToWorld(ent, par2, par3WorldServer, par4WorldServer)
}
}

View File

@ -0,0 +1,2 @@
tile.tinycarts.transparent_bedrock.name=You're not supposed to have this block!
item.tinycarts.cart.name=Expandable Minecart

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B