Merge pull request #513 from EngineHub/bugfix/reloadable-internal-state-ids

Re-load internal state IDs when WORLD_EDITING cap is re-loaded
This commit is contained in:
wizjany 2019-08-03 09:59:10 -04:00 committed by GitHub
commit df9d766eb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 41 additions and 46 deletions

View File

@ -19,6 +19,11 @@
package com.sk89q.worldedit.extension.platform; package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockType;
import com.sk89q.worldedit.world.registry.BlockRegistry;
/** /**
* A collection of capabilities that a {@link Platform} may support. * A collection of capabilities that a {@link Platform} may support.
*/ */
@ -73,7 +78,22 @@ public enum Capability {
/** /**
* The capability of a platform to perform modifications to a world. * The capability of a platform to perform modifications to a world.
*/ */
WORLD_EDITING; WORLD_EDITING {
@Override
void initialize(PlatformManager platformManager, Platform platform) {
BlockRegistry blockRegistry = platform.getRegistries().getBlockRegistry();
for (BlockType type : BlockType.REGISTRY) {
for (BlockState state : type.getAllStates()) {
BlockStateIdAccess.register(state, blockRegistry.getInternalBlockStateId(state));
}
}
}
@Override
void unload(PlatformManager platformManager, Platform platform) {
BlockStateIdAccess.clear();
}
};
void initialize(PlatformManager platformManager, Platform platform) { void initialize(PlatformManager platformManager, Platform platform) {

View File

@ -19,57 +19,46 @@
package com.sk89q.worldedit.internal.block; package com.sk89q.worldedit.internal.block;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map;
import java.util.OptionalInt; import java.util.OptionalInt;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
public final class BlockStateIdAccess { public final class BlockStateIdAccess {
private BlockStateIdAccess() { private static final BiMap<BlockState, Integer> ASSIGNED_IDS = HashBiMap.create(2 << 13);
}
public interface Provider {
OptionalInt getBlockStateId(BlockState holder);
}
private static Provider blockStateStateId;
public static void setBlockStateStateId(Provider blockStateStateId) {
BlockStateIdAccess.blockStateStateId = blockStateStateId;
}
public static OptionalInt getBlockStateId(BlockState holder) { public static OptionalInt getBlockStateId(BlockState holder) {
return blockStateStateId.getBlockStateId(holder); Integer value = ASSIGNED_IDS.get(holder);
return value == null ? OptionalInt.empty() : OptionalInt.of(value);
} }
public static @Nullable BlockState getBlockStateById(int id) { public static @Nullable BlockState getBlockStateById(int id) {
return id < blockStates.length ? blockStates[id] : null; return ASSIGNED_IDS.inverse().get(id);
} }
private static BlockState[] blockStates = new BlockState[2 << 13]; public static void register(BlockState blockState, OptionalInt id) {
public static void register(BlockState blockState) {
OptionalInt id = getBlockStateId(blockState);
if (id.isPresent()) { if (id.isPresent()) {
int i = id.getAsInt(); int i = id.getAsInt();
if (i >= blockStates.length) { BlockState existing = ASSIGNED_IDS.inverse().get(i);
int curLength = blockStates.length;
do {
curLength += curLength >> 1;
} while (i >= curLength);
blockStates = Arrays.copyOf(blockStates, curLength);
}
BlockState existing = blockStates[i];
checkState(existing == null || existing == blockState, checkState(existing == null || existing == blockState,
"BlockState %s is using the same block ID (%s) as BlockState %s", "BlockState %s is using the same block ID (%s) as BlockState %s",
blockState, i, existing); blockState, i, existing);
blockStates[i] = blockState; ASSIGNED_IDS.put(blockState, i);
} }
} }
public static void clear() {
ASSIGNED_IDS.clear();
}
private BlockStateIdAccess() {
}
} }

View File

@ -27,7 +27,6 @@ import com.google.common.collect.Table;
import com.sk89q.jnbt.CompoundTag; import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.internal.block.BlockStateIdAccess;
import com.sk89q.worldedit.registry.state.Property; import com.sk89q.worldedit.registry.state.Property;
import com.sk89q.worldedit.world.registry.BlockRegistry; import com.sk89q.worldedit.world.registry.BlockRegistry;
@ -38,7 +37,6 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.OptionalInt;
import java.util.Set; import java.util.Set;
/** /**
@ -47,13 +45,8 @@ import java.util.Set;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class BlockState implements BlockStateHolder<BlockState> { public class BlockState implements BlockStateHolder<BlockState> {
static {
BlockStateIdAccess.setBlockStateStateId(x -> x.internalId);
}
private final BlockType blockType; private final BlockType blockType;
private final Map<Property<?>, Object> values; private final Map<Property<?>, Object> values;
private OptionalInt internalId = OptionalInt.empty();
private BaseBlock emptyBaseBlock; private BaseBlock emptyBaseBlock;
@ -66,12 +59,6 @@ public class BlockState implements BlockStateHolder<BlockState> {
this.emptyBaseBlock = new BaseBlock(this); this.emptyBaseBlock = new BaseBlock(this);
} }
BlockState initializeId(BlockRegistry registry) {
this.internalId = registry.getInternalBlockStateId(this);
BlockStateIdAccess.register(this);
return this;
}
static Map<Map<Property<?>, Object>, BlockState> generateStateMap(BlockType blockType) { static Map<Map<Property<?>, Object>, BlockState> generateStateMap(BlockType blockType) {
BlockRegistry registry = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getRegistries().getBlockRegistry(); BlockRegistry registry = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING).getRegistries().getBlockRegistry();
Map<Map<Property<?>, Object>, BlockState> stateMap = new LinkedHashMap<>(); Map<Map<Property<?>, Object>, BlockState> stateMap = new LinkedHashMap<>();
@ -94,14 +81,13 @@ public class BlockState implements BlockStateHolder<BlockState> {
valueMap.put(property, value); valueMap.put(property, value);
stateMaker.setState(property, value); stateMaker.setState(property, value);
} }
stateMaker.initializeId(registry);
stateMap.put(valueMap, stateMaker); stateMap.put(valueMap, stateMaker);
} }
} }
if (stateMap.isEmpty()) { if (stateMap.isEmpty()) {
// No properties. // No properties.
stateMap.put(new LinkedHashMap<>(), new BlockState(blockType).initializeId(registry)); stateMap.put(new LinkedHashMap<>(), new BlockState(blockType));
} }
for (BlockState state : stateMap.values()) { for (BlockState state : stateMap.values()) {
@ -114,11 +100,11 @@ public class BlockState implements BlockStateHolder<BlockState> {
private void populate(Map<Map<Property<?>, Object>, BlockState> stateMap) { private void populate(Map<Map<Property<?>, Object>, BlockState> stateMap) {
final Table<Property<?>, Object, BlockState> states = HashBasedTable.create(); final Table<Property<?>, Object, BlockState> states = HashBasedTable.create();
for(final Map.Entry<Property<?>, Object> entry : this.values.entrySet()) { for (final Map.Entry<Property<?>, Object> entry : this.values.entrySet()) {
final Property<Object> property = (Property<Object>) entry.getKey(); final Property<Object> property = (Property<Object>) entry.getKey();
property.getValues().forEach(value -> { property.getValues().forEach(value -> {
if(value != entry.getValue()) { if (value != entry.getValue()) {
BlockState modifiedState = stateMap.get(this.withValue(property, value)); BlockState modifiedState = stateMap.get(this.withValue(property, value));
if (modifiedState != null) { if (modifiedState != null) {
states.put(property, value, modifiedState); states.put(property, value, modifiedState);