Initial Commit.
Has the startings of a large, extensible multiblock library.
This commit is contained in:
commit
46e2dea59f
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal 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
175
build.gradle
Normal 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'
|
||||
}
|
||||
}
|
83
src/main/java/uk/gemwire/engage/Engage.java
Normal file
83
src/main/java/uk/gemwire/engage/Engage.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
19
src/main/java/uk/gemwire/engage/ModSetup.java
Normal file
19
src/main/java/uk/gemwire/engage/ModSetup.java
Normal 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() {
|
||||
|
||||
}
|
||||
}
|
11
src/main/java/uk/gemwire/engage/blocks/Blocks.java
Normal file
11
src/main/java/uk/gemwire/engage/blocks/Blocks.java
Normal 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;
|
||||
|
||||
}
|
115
src/main/java/uk/gemwire/engage/blocks/ModTileEntity.java
Normal file
115
src/main/java/uk/gemwire/engage/blocks/ModTileEntity.java
Normal 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);
|
||||
}
|
||||
}
|
20
src/main/java/uk/gemwire/engage/blocks/util/WCoreStruct.java
Normal file
20
src/main/java/uk/gemwire/engage/blocks/util/WCoreStruct.java
Normal 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");
|
||||
}
|
||||
}
|
10
src/main/java/uk/gemwire/engage/items/ItemProperties.java
Normal file
10
src/main/java/uk/gemwire/engage/items/ItemProperties.java
Normal 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);
|
||||
}
|
38
src/main/java/uk/gemwire/engage/misc/Helper.java
Normal file
38
src/main/java/uk/gemwire/engage/misc/Helper.java
Normal 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);
|
||||
}
|
||||
}
|
241
src/main/java/uk/gemwire/engage/multiblocks/bts/BlockFacing.java
Normal file
241
src/main/java/uk/gemwire/engage/multiblocks/bts/BlockFacing.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
|
||||