Initial Commit.

Has the startings of a large, extensible multiblock library.
This commit is contained in:
Curle 2020-02-28 22:27:29 +00:00
commit 46e2dea59f
29 changed files with 2719 additions and 0 deletions

27
.gitignore vendored Normal file
View File

@ -0,0 +1,27 @@
# eclipse
bin
*.launch
.settings
.metadata
.classpath
.project
# idea
out
*.ipr
*.iws
*.iml
.idea
# gradle
build
.gradle
gradle*
# other
eclipse
run
# Files from Forge MDK
forge*changelog.txt

175
build.gradle Normal file
View File

@ -0,0 +1,175 @@
buildscript {
repositories {
maven { url = 'https://files.minecraftforge.net/maven' }
jcenter()
mavenCentral()
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
}
}
apply plugin: 'net.minecraftforge.gradle'
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
apply plugin: 'eclipse'
apply plugin: 'maven-publish'
version = '1.15.2-0.1'
group = 'uk.gemwire.engage' // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = 'engage'
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
minecraft {
// The mappings can be changed at any time, and must be in the following format.
// snapshot_YYYYMMDD Snapshot are built nightly.
// stable_# Stables are built at the discretion of the MCP team.
// Use non-default mappings at your own risk. they may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
mappings channel: 'snapshot', version: '20190719-1.14.3'
// makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
client {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
mods {
examplemod {
source sourceSets.main
}
}
}
server {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
mods {
examplemod {
source sourceSets.main
}
}
}
data {
workingDirectory project.file('run')
// Recommended logging data for a userdev environment
property 'forge.logging.markers', 'SCAN,REGISTRIES,REGISTRYDUMP'
// Recommended logging level for the console
property 'forge.logging.console.level', 'debug'
args '--mod', 'examplemod', '--all', '--output', file('src/generated/resources/')
mods {
examplemod {
source sourceSets.main
}
}
}
}
}
dependencies {
// Specify the version of Minecraft to use, If this is any group other then 'net.minecraft' it is assumed
// that the dep is a ForgeGradle 'patcher' dependency. And it's patches will be applied.
// The userdev artifact is a special name and will get all sorts of transformations applied to it.
minecraft 'net.minecraftforge:forge:1.15.2-31.1.12'
// You may put jars on which you depend on in ./libs or you may define them like so..
// compile "some.group:artifact:version:classifier"
// compile "some.group:artifact:version"
// Real examples
// compile 'com.mod-buildcraft:buildcraft:6.0.8:dev' // adds buildcraft to the dev env
// compile 'com.googlecode.efficient-java-matrix-library:ejml:0.24' // adds ejml to the dev env
// The 'provided' configuration is for optional dependencies that exist at compile-time but might not at runtime.
// provided 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// These dependencies get remapped to your current MCP mappings
// deobf 'com.mod-buildcraft:buildcraft:6.0.8:dev'
// For more info...
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
}
// Example for how to get properties into the manifest for reading by the runtime..
jar {
manifest {
attributes([
"Specification-Title": "examplemod",
"Specification-Vendor": "examplemodsareus",
"Specification-Version": "1", // We are version 1 of ourselves
"Implementation-Title": project.name,
"Implementation-Version": "${version}",
"Implementation-Vendor" :"examplemodsareus",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
}
}
// Example configuration to allow publishing using the maven-publish task
// This is the preferred method to reobfuscate your jar file
jar.finalizedBy('reobfJar')
// However if you are in a multi-project build, dev time needs unobfed jar files, so you can delay the obfuscation until publishing by doing
//publish.dependsOn('reobfJar')
publishing {
publications {
mavenJava(MavenPublication) {
artifact jar
}
}
repositories {
maven {
url "file:///${project.projectDir}/mcmodsrepo"
}
}
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
build.dependsOn sourcesJar
artifacts {
archives sourcesJar
}
// Process resources on build
processResources {
// This will ensure that this task is redone when the versions change.
inputs.property 'version', project.version
// Replace stuff in mods.toml, nothing else
from(sourceSets.main.resources.srcDirs) {
include 'META-INF/mods.toml'
// Replace version
expand 'version':project.version
}
// Copy everything else except the mods.toml
from(sourceSets.main.resources.srcDirs) {
exclude 'META-INF/mods.toml'
}
}

View File

@ -0,0 +1,83 @@
package uk.gemwire.engage;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.world.chunk.IChunk;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.gemwire.engage.blocks.Blocks;
import uk.gemwire.engage.blocks.util.WCoreStruct;
import uk.gemwire.engage.items.ItemProperties;
import uk.gemwire.engage.multiblocks.bts.MultiblockRegistry;
@Mod("engage")
public class Engage {
public static final Logger LOGGER = LogManager.getLogger();
public static ModSetup SETUP = new ModSetup();
public Engage() {
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
}
private void setup(final FMLCommonSetupEvent e) {
}
@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
public static class RegistryEvents {
@SubscribeEvent
public static void onBlockRegistry(final RegistryEvent.Register<Block> e) {
e.getRegistry().register(new WCoreStruct());
}
@SubscribeEvent
public static void onItemRegistry(final RegistryEvent.Register<Item> e) {
e.getRegistry().register(new BlockItem(Blocks.WCORESTRUCT, ItemProperties.BlockItemProperties).setRegistryName("wcoreextern"));
}
@SubscribeEvent
public void onChunkLoad(final ChunkEvent.Load e) {
IChunk chunk = e.getChunk();
MultiblockRegistry.INSTANCE.onChunkLoaded(e.getWorld().getWorld(), chunk.getPos().x, chunk.getPos().z);
}
@SubscribeEvent
public void onWorldUnload(final WorldEvent.Unload e) {
MultiblockRegistry.INSTANCE.onWorldUnloaded(e.getWorld().getWorld());
}
@SubscribeEvent
public void onWorldTick(final TickEvent.WorldTickEvent e) {
if(e.phase == TickEvent.Phase.START)
MultiblockRegistry.INSTANCE.tickStart(e.world);
}
@OnlyIn(Dist.CLIENT)
@SubscribeEvent
public void onClientTick(final TickEvent.ClientTickEvent e) {
if(e.phase == TickEvent.Phase.START)
MultiblockRegistry.INSTANCE.tickStart(Minecraft.getInstance().world);
}
}
}

View File

@ -0,0 +1,19 @@
package uk.gemwire.engage;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import uk.gemwire.engage.blocks.Blocks;
public class ModSetup {
public ItemGroup tab = new ItemGroup("engage") {
@Override
public ItemStack createIcon() {
return new ItemStack(Blocks.WCORESTRUCT);
}
};
public void init() {
}
}

View File

@ -0,0 +1,11 @@
package uk.gemwire.engage.blocks;
import net.minecraftforge.registries.ObjectHolder;
import uk.gemwire.engage.blocks.util.WCoreStruct;
public class Blocks {
@ObjectHolder("engage:wcoreextern")
public static WCoreStruct WCORESTRUCT;
}

View File

@ -0,0 +1,115 @@
package uk.gemwire.engage.blocks;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.play.server.SUpdateTileEntityPacket;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import uk.gemwire.engage.misc.Helper;
import javax.annotation.Nullable;
public abstract class ModTileEntity extends TileEntity {
public ModTileEntity(TileEntityType<?> type) {
super(type);
}
public boolean isUsableByPlayer(PlayerEntity player) {
BlockPos position = this.getPos();
if(world.getTileEntity(pos) != this)
return false;
return player.getDistanceSq((double) pos.getX() + 0.5D, (double) pos.getY() + 0.5D, (double) pos.getZ() + 0.5D) <= 64D;
}
public boolean canOpenGui(World world, BlockPos pos, BlockState state) {
return false;
}
@OnlyIn(Dist.CLIENT)
public boolean showScreen(Screen screen) {
Minecraft.getInstance().displayGuiScreen(screen);
return true;
}
public enum SyncReason {
FullSync,
NetworkUpdate
}
@Override
public void read(CompoundNBT data) {
super.read(data);
this.syncDataFrom(data, SyncReason.FullSync);
}
@Override
public CompoundNBT write(CompoundNBT data) {
this.syncDataTo(super.write(data), SyncReason.FullSync);
return data;
}
@Override
public void handleUpdateTag(CompoundNBT tag) {
super.read(tag);
this.syncDataFrom(tag, SyncReason.NetworkUpdate);
}
@Override
public CompoundNBT getUpdateTag() {
CompoundNBT data = super.getUpdateTag();
this.syncDataTo(data, SyncReason.NetworkUpdate);
return data;
}
@Override
public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) {
this.syncDataFrom(getUpdatePacket().getNbtCompound(), SyncReason.NetworkUpdate);
}
@Nullable
@Override
public SUpdateTileEntityPacket getUpdatePacket() {
CompoundNBT data = new CompoundNBT();
this.syncDataTo(data, SyncReason.NetworkUpdate);
return new SUpdateTileEntityPacket(this.getPos(), 0, data);
}
protected abstract void syncDataFrom(CompoundNBT data, SyncReason reason);
protected abstract void syncDataTo(CompoundNBT data, SyncReason reason);
public void markChunkDirty() {
this.world.markChunkDirty(this.getPos(), this);
}
public void callNeighborBlockChange() {
this.world.notifyNeighborsOfStateChange(this.getPos(), this.getBlockState().getBlock());
}
public void notifyBlockUpdate() {
Helper.notifyBlockUpdate(this.world, this.getPos(), null, null);
}
public void notifyBlockUpdate(BlockState oldState, BlockState newState) {
Helper.notifyBlockUpdate(this.world, this.getPos(), oldState, newState);
}
public void notifyTileEntityUpdate() {
this.markDirty();
Helper.notifyBlockUpdate(this.world, this.getPos(), null, null);
}
}

View File

@ -0,0 +1,20 @@
package uk.gemwire.engage.blocks.util;
import net.minecraft.block.Block;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.Material;
public class WCoreStruct extends Block {
public WCoreStruct() {
super(Properties
.create(Material.IRON)
.sound(SoundType.STONE)
.hardnessAndResistance(2.0f)
.lightValue(0)
);
setRegistryName("wcoreextern");
}
}

View File

@ -0,0 +1,10 @@
package uk.gemwire.engage.items;
import net.minecraft.item.Item;
import uk.gemwire.engage.Engage;
public class ItemProperties {
public static Item.Properties BlockItemProperties = new Item.Properties()
.group(Engage.SETUP.tab);
}

View File

@ -0,0 +1,38 @@
package uk.gemwire.engage.misc;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
public class Helper {
public static int blockToChunkX(int blockX) {
return blockX >> 4;
}
public static int blockToChunkX(BlockPos pos) {
return pos.getX() >> 4;
}
public static int blockToChunkZ(int blockZ) {
return blockZ >> 4;
}
public static int blockToChunkZ(BlockPos pos) {
return pos.getZ() >> 4;
}
public static void notifyBlockUpdate(World world, BlockPos position, BlockState oldState, BlockState newState) {
if(oldState == null)
oldState = world.getBlockState(position);
if(newState == null)
newState = oldState;
world.notifyBlockUpdate(position, oldState, newState, 3);
}
public static long chunkPosHash(BlockPos pos) {
return ChunkPos.asLong(pos.getX() >> 4, pos.getZ() >> 4);
}
}

View File

@ -0,0 +1,241 @@
package uk.gemwire.engage.multiblocks.bts;
import net.minecraft.block.BlockState;
import net.minecraft.state.BooleanProperty;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import java.util.HashMap;
public final class BlockFacing {
private byte value;
private static HashMap<Byte, BlockFacing> cache;
public static final BlockFacing NONE;
public static final BlockFacing ALL;
public static final BlockFacing DOWN;
public static final BlockFacing UP;
public static final BlockFacing NORTH;
public static final BlockFacing SOUTH;
public static final BlockFacing WEST;
public static final BlockFacing EAST;
public static final BooleanProperty FACING_DOWN = BooleanProperty.create("downFacing");
public static final BooleanProperty FACING_UP = BooleanProperty.create("upFacing");
public static final BooleanProperty FACING_WEST = BooleanProperty.create("westFacing");
public static final BooleanProperty FACING_EAST = BooleanProperty.create("eastFacing");
public static final BooleanProperty FACING_NORTH = BooleanProperty.create("northFacing");
public static final BooleanProperty FACING_SOUTH = BooleanProperty.create("southFacing");
static {
Byte hash;
cache = new HashMap<Byte, BlockFacing>(8);
hash = BlockFacing.computeHash(false, false, false, false, false, false);
cache.put(hash, NONE = new BlockFacing(hash.byteValue()));
hash = BlockFacing.computeHash(true, true, true, true, true, true);
cache.put(hash, ALL = new BlockFacing(hash.byteValue()));
hash = BlockFacing.computeHash(true, false, false, false, false, false);
cache.put(hash, DOWN = new BlockFacing(hash.byteValue()));
hash = BlockFacing.computeHash(false, true, false, false, false, false);
cache.put(hash, UP = new BlockFacing(hash.byteValue()));
hash = BlockFacing.computeHash(false, false, true, false, false, false);
cache.put(hash, NORTH = new BlockFacing(hash.byteValue()));
hash = BlockFacing.computeHash(false, false, false, true, false, false);
cache.put(hash, SOUTH = new BlockFacing(hash.byteValue()));
hash = BlockFacing.computeHash(false, false, false, false, true, false);
cache.put(hash, WEST = new BlockFacing(hash.byteValue()));
hash = BlockFacing.computeHash(false, false, false, false, false, true);
cache.put(hash, EAST = new BlockFacing(hash.byteValue()));
}
public boolean isSet(Direction dir) {
return (this.value & (1 << dir.getIndex())) != 0;
}
public boolean none() {
return this.value == 0;
}
public boolean all() {
return this.value == 0x3f;
}
public boolean down() {
return this.isSet(Direction.DOWN);
}
public boolean up() {
return this.isSet(Direction.UP);
}
public boolean north() {
return this.isSet(Direction.NORTH);
}
public boolean south() {
return this.isSet(Direction.SOUTH);
}
public boolean west() {
return this.isSet(Direction.WEST);
}
public boolean east() {
return this.isSet(Direction.EAST);
}
public BlockState toBlockState(BlockState state) {
return state.with(FACING_DOWN, this.isSet(Direction.DOWN))
.with(FACING_UP, this.isSet(Direction.UP))
.with(FACING_WEST, this.isSet(Direction.WEST))
.with(FACING_EAST, this.isSet(Direction.EAST))
.with(FACING_NORTH, this.isSet(Direction.NORTH))
.with(FACING_SOUTH, this.isSet(Direction.SOUTH));
}
public BlockFacing set(Direction dir, boolean value) {
byte newHash = this.value;
if(value)
newHash |= (1 << dir.getIndex());
else
newHash &= (~1 << dir.getIndex());
return BlockFacing.from(Byte.valueOf(newHash));
}
public int countFacesIf(boolean areSet) {
int checkFor = areSet ? 1 : 0;
int mask = this.value;
int faces = 0;
for(int i = 0; i < 6; ++i, mask = mask >>> 1) {
if((mask & 1) == checkFor)
++faces;
}
return faces;
}
public BlockFacingProperty toProperty() {
BlockFacingProperty[] values = BlockFacingProperty.values();
for(int i = 0; i < values.length; ++i) {
if (values[i].hash == this.value)
return values[i];
}
return BlockFacingProperty.None;
}
public BlockPos offsetBlockPos(BlockPos origPos) {
int x = 0, y = 0, z = 0;
for(Direction direction : Direction.values()) {
if(this.isSet(direction)) {
x += direction.getXOffset();
y += direction.getYOffset();
z += direction.getZOffset();
}
}
return origPos.add(x, y, z);
}
public Direction firstIf(boolean isSet) {
for(Direction dir : Direction.values()) {
if(this.isSet(dir) == isSet)
return dir;
}
return null;
}
public static BlockFacing from(boolean down, boolean up, boolean north, boolean south, boolean west, boolean east) {
return BlockFacing.from(BlockFacing.computeHash(down, up, north, south, west, east));
}
public static BlockFacing from(boolean[] facings) {
return BlockFacing.from(BlockFacing.computeHash(facings));
}
@Override
public String toString() {
return String.format("Facings: %s%s%s%s%s%s",
this.isSet(Direction.DOWN) ? "DOWN " : "",
this.isSet(Direction.UP) ? "UP " : "",
this.isSet(Direction.NORTH) ? "NORTH " : "",
this.isSet(Direction.SOUTH) ? "SOUTH " : "",
this.isSet(Direction.WEST) ? "WEST " : "",
this.isSet(Direction.EAST) ? "EAST " : "");
}
static BlockFacing from(Byte hash) {
BlockFacing facing = BlockFacing.cache.get(hash);
if(facing == null) {
facing = new BlockFacing(hash.byteValue());
BlockFacing.cache.put(hash, facing);
}
return facing;
}
private BlockFacing(byte value) {
this.value = value;
}
static Byte computeHash(boolean down, boolean up, boolean north, boolean south, boolean west, boolean east) {
byte hash = 0;
if (down)
hash |= (1 << Direction.DOWN.getIndex());
if (up)
hash |= (1 << Direction.UP.getIndex());
if (north)
hash |= (1 << Direction.NORTH.getIndex());
if (south)
hash |= (1 << Direction.SOUTH.getIndex());
if (west)
hash |= (1 << Direction.WEST.getIndex());
if (east)
hash |= (1 << Direction.EAST.getIndex());
return Byte.valueOf(hash);
}
static Byte computeHash(boolean[] facings) {
byte hash = 0;
int len = null == facings ? -1 : facings.length;
if (len < 0 || len > Direction.values().length)
throw new IllegalArgumentException("Invalid length of facings array");
for (int i = 0; i < len; ++i) {
if (facings[i])
hash |= (1 << Direction.values()[1].getIndex());
}
return Byte.valueOf(hash);
}
}

View File

@ -0,0 +1,120 @@
package uk.gemwire.engage.multiblocks.bts;
import net.minecraft.state.EnumProperty;
import net.minecraft.util.IStringSerializable;
import java.util.EnumSet;
public enum BlockFacingProperty implements IStringSerializable {
None(BlockFacing.computeHash(false, false, false, false, false, false)),
All(BlockFacing.computeHash(true, true, true, true, true, true)),
Face_D(BlockFacing.computeHash(true, false, false, false, false, false)),
Face_E(BlockFacing.computeHash(false, false, false, false, false, true)),
Face_N(BlockFacing.computeHash(false, false, true, false, false, false)),
Face_S(BlockFacing.computeHash(false, false, false, true, false, false)),
Face_U(BlockFacing.computeHash(false, true, false, false, false, false)),
Face_W(BlockFacing.computeHash(false, false, false, false, true, false)),
Angle_DE(BlockFacing.computeHash(true, false, false, false, false, true)),
Angle_DN(BlockFacing.computeHash(true, false, true, false, false, false)),
Angle_DS(BlockFacing.computeHash(true, false, false, true, false, false)),
Angle_DW(BlockFacing.computeHash(true, false, false, false, true, false)),
Angle_EN(BlockFacing.computeHash(false, false, true, false, false, true)),
Angle_ES(BlockFacing.computeHash(false, false, false, true, false, true)),
Angle_EU(BlockFacing.computeHash(false, true, false, false, false, true)),
Angle_NU(BlockFacing.computeHash(false, true, true, false, false, false)),
Angle_NW(BlockFacing.computeHash(false, false, true, false, true, false)),
Angle_SU(BlockFacing.computeHash(false, true, false, true, false, false)),
Angle_SW(BlockFacing.computeHash(false, false, false, true, true, false)),
Angle_UW(BlockFacing.computeHash(false, true, false, false, true, false)),
Opposite_DU(BlockFacing.computeHash(true, true, false, false, false, false)),
Opposite_EW(BlockFacing.computeHash(false, false, false, false, true, true)),
Opposite_NS(BlockFacing.computeHash(false, false, true, true, false, false)),
CShape_DEU(BlockFacing.computeHash(true, true, false, false, false, true)),
CShape_DEW(BlockFacing.computeHash(true, false, false, false, true, true)),
CShape_DNS(BlockFacing.computeHash(true, false, true, true, false, false)),
CShape_DNU(BlockFacing.computeHash(true, true, true, false, false, false)),
CShape_DSU(BlockFacing.computeHash(true, true, false, true, false, false)),
CShape_DUW(BlockFacing.computeHash(true, true, false, false, true, false)),
CShape_ENS(BlockFacing.computeHash(false, false, true, true, false, true)),
CShape_ENW(BlockFacing.computeHash(false, false, true, false, true, true)),
CShape_ESW(BlockFacing.computeHash(false, false, false, true, true, true)),
CShape_EUW(BlockFacing.computeHash(false, true, false, false, true, true)),
CShape_NSU(BlockFacing.computeHash(false, true, true, true, false, false)),
CShape_NSW(BlockFacing.computeHash(false, false, true, true, true, false)),
Corner_DEN(BlockFacing.computeHash(true, false, true, false, false, true)),
Corner_DES(BlockFacing.computeHash(true, false, false, true, false, true)),
Corner_DNW(BlockFacing.computeHash(true, false, true, false, true, false)),
Corner_DSW(BlockFacing.computeHash(true, false, false, true, true, false)),
Corner_ENU(BlockFacing.computeHash(false, true, true, false, false, true)),
Corner_ESU(BlockFacing.computeHash(false, true, false, true, false, true)),
Corner_NUW(BlockFacing.computeHash(false, true, true, false, true, false)),
Corner_SUW(BlockFacing.computeHash(false, true, false, true, true, false)),
Misc_DENS(BlockFacing.computeHash(true, false, true, true, false, true)),
Misc_DENU(BlockFacing.computeHash(true, true, true, false, false, true)),
Misc_DENW(BlockFacing.computeHash(true, false, true, false, true, true)),
Misc_DESU(BlockFacing.computeHash(true, true, false, true, false, true)),
Misc_DESW(BlockFacing.computeHash(true, false, false, true, true, true)),
Misc_DNSW(BlockFacing.computeHash(true, false, true, true, true, false)),
Misc_DNUW(BlockFacing.computeHash(true, true, true, false, true, false)),
Misc_DSUW(BlockFacing.computeHash(true, true, false, true, true, false)),
Misc_ENSU(BlockFacing.computeHash(false, true, true, true, false, true)),
Misc_ENUW(BlockFacing.computeHash(false, true, true, false, true, true)),
Misc_ESUW(BlockFacing.computeHash(false, true, false, true, true, true)),
Misc_NSUW(BlockFacing.computeHash(false, true, true, true, true, false)),
Pipe_DEUW(BlockFacing.computeHash(true, true, false, false, true, true)),
Pipe_DNSU(BlockFacing.computeHash(true, true, true, true, false, false)),
Pipe_ENSW(BlockFacing.computeHash(false, false, true, true, true, true)),
PipeEnd_DENSU(BlockFacing.computeHash(true, true, true, true, false, true)),
PipeEnd_DENSW(BlockFacing.computeHash(true, false, true, true, true, true)),
PipeEnd_DENUW(BlockFacing.computeHash(true, true, true, false, true, true)),
PipeEnd_DESUW(BlockFacing.computeHash(true, true, false, true, true, true)),
PipeEnd_DNSUW(BlockFacing.computeHash(true, true, true, true, true, false)),
PipeEnd_ENSUW(BlockFacing.computeHash(false, true, true, true, false, true));
public static final EnumProperty FACINGS = EnumProperty.create("facings", BlockFacingProperty.class);
public static final EnumSet<BlockFacingProperty> ALL_AND_NONE;
public static final EnumSet<BlockFacingProperty> FACES;
public static final EnumSet<BlockFacingProperty> ANGLES;
public static final EnumSet<BlockFacingProperty> OPPOSITES;
public static final EnumSet<BlockFacingProperty> CSHAPES;
public static final EnumSet<BlockFacingProperty> CORNERS;
public static final EnumSet<BlockFacingProperty> MISCELLANEA;
public static final EnumSet<BlockFacingProperty> PIPES;
public static final EnumSet<BlockFacingProperty> PIPEENDS;
@Override
public String getName() {
return this.name;
}
BlockFacingProperty(byte hash) {
this.hash = hash;
this.name = this.toString().toLowerCase();
}
static {
ALL_AND_NONE = EnumSet.of(All, None);
FACES = EnumSet.of(Face_D, Face_E, Face_N, Face_S, Face_U, Face_W);
ANGLES = EnumSet.of(Angle_DE, Angle_DN, Angle_DS, Angle_DW, Angle_EN, Angle_ES, Angle_EU, Angle_NU, Angle_NW, Angle_SU, Angle_SW, Angle_UW);
OPPOSITES = EnumSet.of(Opposite_DU, Opposite_EW, Opposite_NS);
CSHAPES = EnumSet.of(CShape_DEU, CShape_DEW, CShape_DNS, CShape_DNU, CShape_DSU, CShape_DUW, CShape_ENS, CShape_ENW, CShape_ESW, CShape_EUW, CShape_NSU, CShape_NSW);
CORNERS = EnumSet.of(Corner_DEN, Corner_DES, Corner_DNW, Corner_DSW, Corner_ENU, Corner_ESU, Corner_NUW, Corner_SUW);
MISCELLANEA = EnumSet.of(Misc_DENS, Misc_DENU, Misc_DENW, Misc_DESU, Misc_DESW, Misc_DNSW, Misc_DNUW, Misc_DSUW, Misc_ENSU, Misc_ENUW, Misc_ESUW, Misc_NSUW);
PIPES = EnumSet.of(Pipe_DEUW, Pipe_DNSU, Pipe_ENSW);
PIPEENDS = EnumSet.of(PipeEnd_DENSU, PipeEnd_DENSW, PipeEnd_DENUW, PipeEnd_DESUW, PipeEnd_DNSUW, PipeEnd_ENSUW);
}
final byte hash;
private final String name;
}

View File

@ -0,0 +1,79 @@
package uk.gemwire.engage.multiblocks.bts;
import net.minecraft.world.World;
import uk.gemwire.engage.Engage;
import uk.gemwire.engage.multiblocks.iface.IMultiblockPart;
import uk.gemwire.engage.multiblocks.iface.IMultiblockRegistry;
import uk.gemwire.engage.multiblocks.te.MultiblockControllerBase;
import java.util.HashMap;
public class MultiblockRegistry implements IMultiblockRegistry {
private HashMap<World, MultiblockWorldRegistry> registries;
public static final MultiblockRegistry INSTANCE = new MultiblockRegistry();
private MultiblockRegistry() {
this.registries = new HashMap<>(2);
}
@Override
public void onPartAdded(final World world, final IMultiblockPart part) {
MultiblockWorldRegistry reg;
if(this.registries.containsKey(world)) {
reg = this.registries.get(world);
} else {
reg = new MultiblockWorldRegistry(world);
this.registries.put(world, reg);
}
reg.onPartAdded(part);
}
@Override
public void onPartRemoved(final World world, final IMultiblockPart part) {
if(this.registries.containsKey(world))
this.registries.get(world).onPartRemoved(part);
}
@Override
public void addDeadController(final World world, final MultiblockControllerBase controller) {
if(this.registries.containsKey(world))
this.registries.get(world).addDeadController(controller);
else
Engage.LOGGER.warn("Multiblock controller %d in world %d exists in an untracked world - assuming a glitch.", controller.hashCode(), world);
}
@Override
public void addDirtyController(final World world, final MultiblockControllerBase controller) {
if(this.registries.containsKey(world))
this.registries.get(world).addDirtyController(controller);
else
throw new IllegalArgumentException("Tried to update a controller in a world we don't track");
}
public void tickStart(final World world) {
if(this.registries.containsKey(world)) {
final MultiblockWorldRegistry reg = this.registries.get(world);
reg.processMultiblockChanges();
reg.tickStart();
}
}
public void onChunkLoaded(final World world, final int chunkX, final int chunkZ) {
if(this.registries.containsKey(world))
this.registries.get(world).onChunkLoaded(chunkX, chunkZ);
}
public void onWorldUnloaded(final World world) {
if(this.registries.containsKey(world)) {
this.registries.get(world).onWorldUnloaded();
this.registries.remove(world);
}
}
}

View File

@ -0,0 +1,293 @@
package uk.gemwire.engage.multiblocks.bts;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.AbstractChunkProvider;
import uk.gemwire.engage.Engage;
import uk.gemwire.engage.misc.Helper;
import uk.gemwire.engage.multiblocks.iface.IMultiblockPart;
import uk.gemwire.engage.multiblocks.te.MultiblockControllerBase;
import java.util.*;
final class MultiblockWorldRegistry {
private World worldObj;
private final Set<MultiblockControllerBase> controllers;
private final Set<MultiblockControllerBase> dirtyControllers;
private final Set<MultiblockControllerBase> deadControllers;
private Set<IMultiblockPart> orphanedParts;
private final Set<IMultiblockPart> detachedParts;
private final HashMap<Long, Set<IMultiblockPart>> partsAwaitingChunkLoad;
private final Object partsACLMutex;
private final Object orphanedPartsMutex;
public MultiblockWorldRegistry(final World world) {
worldObj = world;
controllers = new HashSet<MultiblockControllerBase>();
dirtyControllers = new HashSet<MultiblockControllerBase>();
deadControllers = new HashSet<MultiblockControllerBase>();
detachedParts = new HashSet<IMultiblockPart>();
orphanedParts = new HashSet<IMultiblockPart>();
partsAwaitingChunkLoad = new HashMap<Long, Set<IMultiblockPart>>();
partsACLMutex = new Object();
orphanedPartsMutex = new Object();
}
public void tickStart() {
if(controllers.size() > 0) {
for(MultiblockControllerBase controller : controllers) {
if(controller.WORLD == worldObj && controller.WORLD.isRemote == worldObj.isRemote) {
if(controller.isEmpty())
deadControllers.add(controller);
else
controller.updateMultiblockEntity();
}
}
}
}
public void processMultiblockChanges() {
BlockPos coord;
List<Set<MultiblockControllerBase>> mergePools = null;
if(orphanedParts.size() > 0) {
Set<IMultiblockPart> orphansToProcess = null;
synchronized (orphanedPartsMutex) {
if(orphanedParts.size() > 0) {
orphansToProcess = orphanedParts;
orphanedParts = new HashSet<IMultiblockPart>();
}
}
if(orphansToProcess != null && orphansToProcess.size() > 0) {
AbstractChunkProvider chunkProvider = this.worldObj.getChunkProvider();
Set<MultiblockControllerBase> compatibleControllers;
for(IMultiblockPart orphan : orphansToProcess) {
coord = orphan.getWorldPosition();
if(!this.worldObj.isBlockLoaded(coord)) continue;
if(!orphan.isPartValid()) continue;
if(this.getMultiblockPartFromWorld(worldObj, coord) != orphan) continue;
compatibleControllers = orphan.attachToControllers();
if(compatibleControllers == null) {
MultiblockControllerBase newController = orphan.createNewMultiblock();
newController.attachBlock(orphan);
this.controllers.add(newController);
} else if(compatibleControllers.size() > 1) {
if(mergePools == null) mergePools = new ArrayList<Set<MultiblockControllerBase>>();
boolean addedToPool = false;
List<Set<MultiblockControllerBase>> candidatePools = new ArrayList<Set<MultiblockControllerBase>>();
for(Set<MultiblockControllerBase> pool : mergePools) {
if(!Collections.disjoint(pool, compatibleControllers))
candidatePools.add(pool);
}
if(candidatePools.size() <= 0)
mergePools.add(compatibleControllers);
else if(candidatePools.size() == 1)
candidatePools.get(0).addAll(compatibleControllers);
else {
Set<MultiblockControllerBase> masterPool = candidatePools.get(0);
Set<MultiblockControllerBase> consumedPool;
for(int i = 1; i < candidatePools.size(); i++) {
consumedPool = candidatePools.get(i);
masterPool.addAll(consumedPool);
mergePools.remove(consumedPool);
}
masterPool.addAll(compatibleControllers);
}
}
}
}
}
if(mergePools != null && mergePools.size() > 0) {
for(Set<MultiblockControllerBase> mergePool : mergePools) {
MultiblockControllerBase master = null;
for(MultiblockControllerBase controller : mergePool) {
if(master == null || controller.shouldConsume(master)) {
master = controller;
}
}
if(master == null)
Engage.LOGGER.error("Multiblock system checked a merge candidate with no controller - this is impossible.");
else {
addDirtyController(master);
for(MultiblockControllerBase controller : mergePool) {
if(controller != master) {
master.assimilate(controller);
addDeadController(controller);
addDirtyController(master);
}
}
}
}
}
if(dirtyControllers.size() > 0) {
Set<IMultiblockPart> newlyDetachedParts = null;
for(MultiblockControllerBase controller : dirtyControllers) {
newlyDetachedParts = controller.checkForDisconnections();
if(!controller.isEmpty()) {
controller.recalculateBoundingBox();
controller.checkAssembled();
} else
addDeadController(controller);
if(newlyDetachedParts != null && newlyDetachedParts.size() > 0)
detachedParts.addAll(newlyDetachedParts);
}
dirtyControllers.clear();
}
if(deadControllers.size() > 0) {
for(MultiblockControllerBase controller : deadControllers) {
if(!controller.isEmpty()) {
Engage.LOGGER.error("Multiblock system killed a controller that still has parts. Forcing it to eat shit and die, because that's impossible.");
detachedParts.addAll(controller.detachAllBlocks());
}
this.controllers.remove(controller);
}
deadControllers.clear();
}
for(IMultiblockPart part : detachedParts)
part.assertDetached();
addAllOrphanedParts(detachedParts);
detachedParts.clear();
}
public void onPartAdded(final IMultiblockPart part) {
BlockPos worldLocation = part.getWorldPosition();
if(!this.worldObj.isBlockLoaded(worldLocation)) {
Set<IMultiblockPart> parts;
long chunkHash = Helper.chunkPosHash(worldLocation);
synchronized(partsACLMutex) {
if(!partsAwaitingChunkLoad.containsKey(chunkHash)) {
parts = new HashSet<IMultiblockPart>();
partsAwaitingChunkLoad.put(chunkHash, parts);
} else
parts = partsAwaitingChunkLoad.get(chunkHash);
parts.add(part);
}
} else
addOrphanedPart(part);
}
public void onPartRemoved(final IMultiblockPart part) {
final BlockPos loc = part.getWorldPosition();
if(loc != null) {
long chunkHash = Helper.chunkPosHash(loc);
synchronized (partsACLMutex) {
if(partsAwaitingChunkLoad.containsKey(chunkHash)) {
partsAwaitingChunkLoad.get(chunkHash).remove(part);
if(partsAwaitingChunkLoad.get(chunkHash).size() <= 0)
partsAwaitingChunkLoad.remove(chunkHash);
}
}
}
detachedParts.remove(part);
synchronized (orphanedPartsMutex) {
orphanedParts.remove(part);
}
part.assertDetached();
}
public void onWorldUnloaded() {
controllers.clear();
deadControllers.clear();
dirtyControllers.clear();
detachedParts.clear();
synchronized (partsACLMutex) {
partsAwaitingChunkLoad.clear();
}
synchronized (orphanedPartsMutex) {
orphanedParts.clear();
}
worldObj = null;
}
public void onChunkLoaded(final int chunkX, final int chunkZ) {
final long chunkHash = ChunkPos.asLong(chunkX, chunkZ);
synchronized (partsACLMutex) {
if(partsAwaitingChunkLoad.containsKey(chunkHash)) {
addAllOrphanedParts(partsAwaitingChunkLoad.get(chunkHash));
partsAwaitingChunkLoad.remove(chunkHash);
}
}
}
public void addDeadController(MultiblockControllerBase controller) {
this.deadControllers.add(controller);
}
public void addDirtyController(MultiblockControllerBase controller) {
this.dirtyControllers.add(controller);
}
public Set<MultiblockControllerBase> getControllers() {
return Collections.unmodifiableSet(controllers);
}
private void addOrphanedPart(final IMultiblockPart part) {
synchronized (orphanedPartsMutex) {
orphanedParts.add(part);
}
}
private void addAllOrphanedParts(final Collection<? extends IMultiblockPart> parts) {
synchronized (orphanedPartsMutex) {
orphanedParts.addAll(parts);
}
}
protected IMultiblockPart getMultiblockPartFromWorld(final World world, final BlockPos pos) {
TileEntity te = world.getTileEntity(pos);
return te instanceof IMultiblockPart ? (IMultiblockPart) te : null;
}
}

View File

@ -0,0 +1,61 @@
package uk.gemwire.engage.multiblocks.bts;
import net.minecraft.state.EnumProperty;
import net.minecraft.util.Direction;
import net.minecraft.util.IStringSerializable;
public enum PartPosition implements IStringSerializable {
Unknown(null, Type.Unknown),
Interior(null, Type.Unknown),
FrameCorner(null, Type.Frame),
FrameEastWest(null, Type.Frame),
FrameSouthNorth(null, Type.Frame),
FrameUpDown(null, Type.Frame),
TopFace(Direction.UP, Type.Face),
BottomFace(Direction.DOWN, Type.Face),
NorthFace(Direction.NORTH, Type.Face),
SouthFace(Direction.SOUTH, Type.Face),
EastFace(Direction.EAST, Type.Face),
WestFace(Direction.WEST, Type.Face);
public enum Type {
Unknown,
Interior,
Frame,
Face
}
public boolean isFace() {
return this.type == Type.Face;
}
public boolean isFrame() {
return this.type == Type.Frame;
}
public Direction getDir() {
return this.dir;
}
public Type getType() {
return this.type;
}
public static EnumProperty createProperty(String name) {
return EnumProperty.create(name, PartPosition.class);
}
@Override
public String getName() {
return this.toString();
}
PartPosition(Direction dir, Type type) {
this.dir = dir;
this.type = type;
}
private Direction dir;
private Type type;
}

View File

@ -0,0 +1,62 @@
package uk.gemwire.engage.multiblocks.iface;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.BlockPos;
import uk.gemwire.engage.multiblocks.te.MultiblockControllerBase;
import java.util.Set;
public interface IMultiblockPart {
boolean isConnected();
MultiblockControllerBase getMultiblockController();
BlockPos getWorldPosition();
boolean isPartValid();
void onAttached(MultiblockControllerBase newController);
void onDetached(MultiblockControllerBase controller);
void onOrphaned(MultiblockControllerBase controller, int oldMultiblockSize, int newMultiblockSize);
MultiblockControllerBase createNewMultiblock();
Class<? extends MultiblockControllerBase> getMultiblockControllerType();
void onAssimilated(MultiblockControllerBase newController);
void setVisited();