diff --git a/build.gradle b/build.gradle index 38dcffa9d..e93e097cb 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'application' apply plugin: 'maven-publish' group 'org.innercore.zotecoreloader' -version 'beta-1.1.1' +version 'beta-1.2.0' application { mainClass = 'com.reider745.Main' diff --git a/codegen/src/main/java/org/innercore/codegen/BindingOverload.java b/codegen/src/main/java/org/innercore/codegen/BindingOverload.java index 7709e64e4..57868b134 100644 --- a/codegen/src/main/java/org/innercore/codegen/BindingOverload.java +++ b/codegen/src/main/java/org/innercore/codegen/BindingOverload.java @@ -17,4 +17,22 @@ public final class BindingOverload { public boolean needJavaWrap = false; public String callSrc = ""; public List attrs = new ArrayList(); + + @Override + public String toString() { + return "BindingOverload{" + + "name='" + name + '\'' + + ", moduleName='" + moduleName + '\'' + + ", mangledName='" + mangledName + '\'' + + ", bindType='" + bindType + '\'' + + ", moduleClsName='" + moduleClsName + '\'' + + ", fnType='" + fnType + '\'' + + ", ret=" + ret + + ", args=" + args + + ", argsCnt=" + argsCnt + + ", needJavaWrap=" + needJavaWrap + + ", callSrc='" + callSrc + '\'' + + ", attrs=" + attrs + + '}'; + } } diff --git a/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java b/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java index 966252e2b..bf62b6347 100644 --- a/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java +++ b/codegen/src/main/java/org/innercore/codegen/JavaBindingGenerator.java @@ -565,7 +565,8 @@ private void generateDefaultedOverloadMethod(BindingOverload overload) { callArgs.add(overload.args.get(i).def); } } - generateWrapMethod(overload, overload.name, overload.name, callArgs); + + generateWrapMethod(overload, overload.name, overload.callSrc, callArgs); } private void generateConstructor(String genClassName, BindingOverload overload) { diff --git a/codegen/src/main/java/org/innercore/codegen/JavaBindingResolver.java b/codegen/src/main/java/org/innercore/codegen/JavaBindingResolver.java index edfdfed5d..5d3a8fab1 100644 --- a/codegen/src/main/java/org/innercore/codegen/JavaBindingResolver.java +++ b/codegen/src/main/java/org/innercore/codegen/JavaBindingResolver.java @@ -23,10 +23,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -100,6 +97,15 @@ private Map getJavaAnnotation(NodeWithAnnotations node, Strin return result; } + private String getAnnotation(NodeWithAnnotations node, String annName) { + for (AnnotationExpr ann : node.getAnnotations()) { + if (ann instanceof SingleMemberAnnotationExpr && simpleAnnotationName(ann).equals(annName) && ((SingleMemberAnnotationExpr) ann).getMemberValue() instanceof StringLiteralExpr) { + return ((StringLiteralExpr) ((SingleMemberAnnotationExpr) ann).getMemberValue()).asString(); + } + } + return ""; + } + private boolean hasAnnotation(NodeWithAnnotations node, String annName) { for (AnnotationExpr ann : node.getAnnotations()) { if (simpleAnnotationName(ann).equals(annName)) { @@ -218,6 +224,7 @@ private VarDecl resolveParameter(Parameter param, ClassOrInterfaceDeclaration cl VarDecl d = new VarDecl(typ); d.name = param.getNameAsString(); d.nullable = hasAnnotation(param, "Nullable"); + d.def = getAnnotation(param, "ArgOverload"); return d; } diff --git a/src/main/java/com/reider745/api/pointers/pointer_gen/PointerGenSlowest.java b/src/main/java/com/reider745/api/pointers/pointer_gen/PointerGenSlowest.java index 966caa6db..ab5e7a240 100644 --- a/src/main/java/com/reider745/api/pointers/pointer_gen/PointerGenSlowest.java +++ b/src/main/java/com/reider745/api/pointers/pointer_gen/PointerGenSlowest.java @@ -11,19 +11,15 @@ public class PointerGenSlowest implements IBasePointerGen { public final long next() { while (true){ final long ptr = random.nextLong(); - synchronized (usePointers) { - if (!usePointers.contains(ptr)) { - usePointers.add(ptr); - return ptr; - } + if (ptr != 0 && !usePointers.contains(ptr)) { + usePointers.add(ptr); + return ptr; } } } @Override public final void remove(long ptr) { - synchronized (usePointers) { - usePointers.remove(ptr); - } + usePointers.remove(ptr); } } \ No newline at end of file diff --git a/src/main/java/com/reider745/block/BlockMethods.java b/src/main/java/com/reider745/block/BlockMethods.java index b8fb47b22..8791cdc59 100644 --- a/src/main/java/com/reider745/block/BlockMethods.java +++ b/src/main/java/com/reider745/block/BlockMethods.java @@ -80,6 +80,7 @@ private static T validateThen(int id, IApply function, T fallback) { } public static int getMaterial(int id) { + InnerCoreServer.useNotCurrentSupport("BlockMethods.getMaterial"); return 0; } diff --git a/src/main/java/com/reider745/block/CustomBlock.java b/src/main/java/com/reider745/block/CustomBlock.java index 06b52ad07..ff208731a 100644 --- a/src/main/java/com/reider745/block/CustomBlock.java +++ b/src/main/java/com/reider745/block/CustomBlock.java @@ -158,7 +158,7 @@ public static CustomManager registerBlock(String textId, int id, String name, Cl customBlocks.put(textId, id); CustomManager.put(id, manager); - NativeItem.newNativeItem(id, manager, textId, name); + NativeItem.constructBlock(id, manager, textId, name); return manager; } diff --git a/src/main/java/com/reider745/commands/CallbackProfilingCommand.java b/src/main/java/com/reider745/commands/CallbackProfilingCommand.java deleted file mode 100644 index 6a8cabf42..000000000 --- a/src/main/java/com/reider745/commands/CallbackProfilingCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.reider745.commands; - -import cn.nukkit.command.Command; -import cn.nukkit.command.CommandSender; -import cn.nukkit.utils.TextFormat; - -import com.zhekasmirnov.innercore.api.runtime.Callback; - -public class CallbackProfilingCommand extends Command { - - public CallbackProfilingCommand() { - super("profilecallback", "Logging callback execution profiling.", - "/profilecallback [enabled] [showParameters]"); - } - - @Override - public boolean execute(CommandSender commandSender, String commandLabel, String[] args) { - if (!commandSender.isOp()) - return false; - - boolean enabled = false, showParameters = false; - if (args.length == 0) { - enabled = !Callback.profilingEnabled; - } else { - enabled = "true".equalsIgnoreCase(args[0]); - if (args.length > 1) { - showParameters = "true".equalsIgnoreCase(args[1]); - if (Callback.profilingShowParameters != (Callback.profilingShowParameters = showParameters)) { - commandSender.sendMessage( - TextFormat.YELLOW + "Parameter logging " + (showParameters ? "enabled" : "disabled") + "."); - } - } - } - if (Callback.profilingEnabled != (Callback.profilingEnabled = enabled)) { - commandSender - .sendMessage(TextFormat.YELLOW + "Callback profiling " + (enabled ? "enabled" : "disabled") + "."); - if (!enabled && !showParameters) - Callback.profilingShowParameters = false; - } - - return true; - } -} diff --git a/src/main/java/com/reider745/commands/CommandsHelper.java b/src/main/java/com/reider745/commands/CommandsHelper.java index f2ca78096..c82c63446 100644 --- a/src/main/java/com/reider745/commands/CommandsHelper.java +++ b/src/main/java/com/reider745/commands/CommandsHelper.java @@ -14,7 +14,6 @@ public static void init() { map.register(tag, new StateCommand()); map.register(tag, new InnerCoreNetworkCommand()); map.register(tag, new GenChunksCommands()); - map.register(tag, new CallbackProfilingCommand()); map.register(tag, new GetAllPlayersIdCommand()); map.register(tag, new DimensionsList()); map.register(tag, new TpsCommand()); diff --git a/src/main/java/com/reider745/commands/GenChunksCommands.java b/src/main/java/com/reider745/commands/GenChunksCommands.java index d12d049bf..9f514541b 100644 --- a/src/main/java/com/reider745/commands/GenChunksCommands.java +++ b/src/main/java/com/reider745/commands/GenChunksCommands.java @@ -5,7 +5,7 @@ import cn.nukkit.command.data.CommandParamType; import cn.nukkit.command.data.CommandParameter; import cn.nukkit.level.Level; -import com.reider745.world.BlockSourceMethods; +import com.reider745.world.FakeDimensions; public class GenChunksCommands extends Command { public GenChunksCommands() { @@ -40,7 +40,7 @@ public boolean execute(CommandSender commandSender, String commandLabel, String[ Level level; try { - level = BlockSourceMethods.getLevelForDimension(dimension); + level = FakeDimensions.getLevelForFakeId(dimension); } catch (UnsupportedOperationException e) { return false; } diff --git a/src/main/java/com/reider745/entity/EntityMethod.java b/src/main/java/com/reider745/entity/EntityMethod.java index eca35b192..c60d878c1 100644 --- a/src/main/java/com/reider745/entity/EntityMethod.java +++ b/src/main/java/com/reider745/entity/EntityMethod.java @@ -31,12 +31,11 @@ import com.reider745.event.EventListener; import com.reider745.hooks.entity.EntityOverrides; import com.reider745.hooks.item.ItemUtils; -import com.reider745.world.BlockSourceMethods; import com.reider745.world.FakeDimensions; import com.zhekasmirnov.apparatus.minecraft.enums.GameEnums; -import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; import com.zhekasmirnov.innercore.api.constants.EntityType; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.innercore.icstd.region.BlockSource; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -208,8 +207,12 @@ public static void setPositionAxis(long entityUid, int axis, float val) { } public static void playSoundEnt(long entityUid, String name, float volume, float pitch) { - validateThen(entityUid, entity -> BlockSourceMethods.playSound(entity.getLevel(), name, (int) entity.x, - (int) entity.y, (int) entity.z, volume, pitch, null)); + validateThen(entityUid, entity -> { + BlockSource blockSource = BlockSource.getDefaultForDimension(FakeDimensions.getFakeIdForLevel(entity.level)); + if(blockSource != null) { + blockSource.playSound((float) entity.x, (float) entity.y, (float) entity.z, name, volume, pitch); + } + }); } public static int getHealth(long entityUid) { @@ -243,11 +246,11 @@ public static Item getEntityArmor(long entityUid, int slot) { public static void setEntityCarriedItem(long entityUid, int id, int count, int data, Item extra) { - setEntityCarriedItem(entityUid, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setEntityCarriedItem(entityUid, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } public static void setEntityCarriedItem(long entityUid, int id, int count, int data, - NativeItemInstanceExtra extra) { + ItemInstanceExtra extra) { // TODO: Human actually player, there is no way to check/replace // TODO: regular entity, properties hardcoded at spawn. validateInventoryThen(entityUid, (enity, inv) -> inv.setItemInHand(ItemUtils.get(id, count, data, extra)), null); @@ -255,11 +258,11 @@ public static void setEntityCarriedItem(long entityUid, int id, int count, int d public static void setEntityOffhandItem(long entityUid, int id, int count, int data, Item extra) { - setEntityOffhandItem(entityUid, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setEntityOffhandItem(entityUid, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } public static void setEntityOffhandItem(long entityUid, int id, int count, int data, - NativeItemInstanceExtra extra) { + ItemInstanceExtra extra) { // TODO: Human actually player, there is no way to check/replace // TODO: regular entity, properties hardcoded at spawn. @@ -268,11 +271,11 @@ public static void setEntityOffhandItem(long entityUid, int id, int count, int d public static void setEntityArmor(long entityUid, int slot, int id, int count, int data, Item extra) { - setEntityArmor(entityUid, slot, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setEntityArmor(entityUid, slot, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } public static void setEntityArmor(long entityUid, int slot, int id, int count, int data, - NativeItemInstanceExtra extra) { + ItemInstanceExtra extra) { // TODO: Human actually player, there is no way to check/replace // TODO: regular entity, properties hardcoded at spawn. validateInventoryThen(entityUid, (enity, inv) -> inv.setArmorItem(slot, ItemUtils.get(id, count, data, extra)), null); @@ -338,10 +341,10 @@ public static Item getItemFromProjectile(long entityUid) { } public static void setItemToDrop(long entityUid, int id, int count, int data, Item extra) { - setItemToDrop(entityUid, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setItemToDrop(entityUid, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setItemToDrop(long entityUid, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setItemToDrop(long entityUid, int id, int count, int data, ItemInstanceExtra extra) { // TODO: It will actually doesn't update item, hardcode again. validateThen(entityUid, entity -> { if (entity instanceof EntityItem drop) { @@ -558,11 +561,11 @@ public static void dealDamage(long entityUid, int damage, int cause, long attack public static void invokeUseItemOn(int id, int count, int data, Item extra, int x, int y, int z, int side, float vx, float vy, float vz, long entityUid) { - invokeUseItemOn(id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra), x, y, z, side, vx, vy, vz, + invokeUseItemOn(id, count, data, ItemInstanceExtra.getExtraOrNull(extra), x, y, z, side, vx, vy, vz, entityUid); } - public static void invokeUseItemOn(int id, int count, int data, NativeItemInstanceExtra extra, int x, int y, int z, + public static void invokeUseItemOn(int id, int count, int data, ItemInstanceExtra extra, int x, int y, int z, int side, float vx, float vy, float vz, long entityUid) { validateThen(entityUid, entity -> { synchronized (EventListener.DEALING_LOCK) { @@ -576,7 +579,7 @@ public static void invokeUseItemOn(int id, int count, int data, NativeItemInstan public static void transferToDimension(long entityUid, int dimension) { validateThen(entityUid, entity -> entity - .setPosition(entity.getPosition().setLevel(BlockSourceMethods.getLevelForDimension(dimension)))); + .setPosition(entity.getPosition().setLevel(FakeDimensions.getLevelForFakeId(dimension)))); } public static int getExperienceOrbValue(long entityUid) { diff --git a/src/main/java/com/reider745/entity/PlayerActorMethods.java b/src/main/java/com/reider745/entity/PlayerActorMethods.java index 5db1c4c82..5a2ff32c4 100644 --- a/src/main/java/com/reider745/entity/PlayerActorMethods.java +++ b/src/main/java/com/reider745/entity/PlayerActorMethods.java @@ -18,7 +18,7 @@ import com.reider745.event.EventListener; import com.reider745.hooks.item.ItemUtils; import com.reider745.world.FakeDimensions; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; public class PlayerActorMethods { public static boolean isValid(Player player, boolean checkOnline) { @@ -32,10 +32,10 @@ public static boolean isValid(Player player, boolean checkOnline) { } public static void invokeUseItemNoTarget(Player player, int id, int count, int data, Item extra) { - invokeUseItemNoTarget(player, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + invokeUseItemNoTarget(player, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void invokeUseItemNoTarget(Player player, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void invokeUseItemNoTarget(Player player, int id, int count, int data, ItemInstanceExtra extra) { if (!isValid(player, false)) { return; } @@ -88,10 +88,10 @@ public static void invokeUseItemNoTarget(Player player, int id, int count, int d } public static void addItemToInventory(Player player, int id, int count, int data, Item extra, boolean dropLeft) { - addItemToInventory(player, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra), dropLeft); + addItemToInventory(player, id, count, data, ItemInstanceExtra.getExtraOrNull(extra), dropLeft); } - public static void addItemToInventory(Player player, int id, int count, int data, NativeItemInstanceExtra extra, boolean dropLeft) { + public static void addItemToInventory(Player player, int id, int count, int data, ItemInstanceExtra extra, boolean dropLeft) { if (!isValid(player, false)) { return; } @@ -210,10 +210,10 @@ public static int getSelectedSlot(Player player) { } public static void setInventorySlot(Player player, int slot, int id, int count, int data, Item extra) { - setInventorySlot(player, slot, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setInventorySlot(player, slot, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setInventorySlot(Player player, int slot, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setInventorySlot(Player player, int slot, int id, int count, int data, ItemInstanceExtra extra) { if (!isValid(player, false)) { return; } @@ -221,10 +221,10 @@ public static void setInventorySlot(Player player, int slot, int id, int count, } public static void setArmor(Player player, int slot, int id, int count, int data, Item extra) { - setArmor(player, slot, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setArmor(player, slot, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setArmor(Player player, int slot, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setArmor(Player player, int slot, int id, int count, int data, ItemInstanceExtra extra) { if (!isValid(player, false)) { return; } diff --git a/src/main/java/com/reider745/event/EventListener.java b/src/main/java/com/reider745/event/EventListener.java index ffaf7b921..3ce6d2399 100644 --- a/src/main/java/com/reider745/event/EventListener.java +++ b/src/main/java/com/reider745/event/EventListener.java @@ -17,10 +17,7 @@ import cn.nukkit.event.entity.EntityDamageEvent.DamageCause; import cn.nukkit.event.inventory.CraftItemEvent; import cn.nukkit.event.inventory.InventoryPickupItemEvent; -import cn.nukkit.event.level.ChunkLoadEvent; -import cn.nukkit.event.level.ChunkPopulateEvent; -import cn.nukkit.event.level.ChunkUnloadEvent; -import cn.nukkit.event.level.LevelUnloadEvent; +import cn.nukkit.event.level.*; import cn.nukkit.event.player.*; import cn.nukkit.event.plugin.PluginDisableEvent; import cn.nukkit.event.plugin.PluginEnableEvent; @@ -71,7 +68,7 @@ import com.zhekasmirnov.apparatus.multiplayer.server.ConnectedClient; import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.api.NativeCallback; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; public class EventListener implements Listener { public static final Object DEALING_LOCK = new Object(); @@ -186,7 +183,7 @@ public void onProjectileHit(ProjectileHitEvent event) { final Vector3 hit = mop != null ? mop.hitVector : entity.getPosition(); long id = EntityMethod.getIdForEntity(entity); final Item item = EntityMethod.getItemFromProjectile(id); - final NativeItemInstanceExtra extra = ItemUtils.getItemInstanceExtra(item); + final ItemInstanceExtra extra = ItemUtils.getItemInstanceExtra(item); consumeEvent(event, () -> NativeCallback.onThrowableHit(id, (float) hit.x, (float) hit.y, (float) hit.z, @@ -617,9 +614,18 @@ public void onChunkUnload(ChunkUnloadEvent event){ NativeCallback.onChunkDiscarded(FakeDimensions.getFakeIdForLevel(event.getLevel()), chunk.getX(), chunk.getZ()); } + @EventHandler + public void onLoadLevel(LevelLoadEvent event) { + NativeCallback.onDimensionLoaded(FakeDimensions.getFakeIdForLevel(event.getLevel())); + } + @EventHandler public void onUnloadLevel(LevelUnloadEvent event){ - RedstoneSystem.onUnloadLevel(event.getLevel()); + final Level level = event.getLevel(); + + RedstoneSystem.onUnloadLevel(level); + NativeCallback.onDimensionUnloaded(FakeDimensions.getFakeIdForLevel(level)); + FakeDimensions.unload(level); } @ZoteOnly diff --git a/src/main/java/com/reider745/hooks/item/ItemUtils.java b/src/main/java/com/reider745/hooks/item/ItemUtils.java index 4d648c213..ce5cbe410 100644 --- a/src/main/java/com/reider745/hooks/item/ItemUtils.java +++ b/src/main/java/com/reider745/hooks/item/ItemUtils.java @@ -21,8 +21,9 @@ import com.reider745.item.NukkitIdConverter; import com.reider745.item.Repairs; import com.zhekasmirnov.innercore.api.NativeFurnaceRegistry; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.unlimited.IDRegistry; +import org.innercore.icstd.inventory.ItemInstanceExtra; import java.util.ArrayList; import java.util.List; @@ -269,8 +270,8 @@ public static void getFuelTime(HookController controller) { public static final String INNER_CORE_TAG_NAME = "$mod"; - public static Item get(int id, int count, int meta, NativeItemInstanceExtra extra) { - Item item = get(id, count, meta, extra != null ? extra.getValue() : null); + public static Item get(int id, int count, int meta, ItemInstanceExtra extra) { + Item item = get(id, count, meta, extra != null ? extra.getItem() : null); if (extra != null) { extra.bind(item); } @@ -301,10 +302,10 @@ public static Item get(int id) { return get(id, 0); } - public static NativeItemInstanceExtra getItemInstanceExtra(Item item) { + public static ItemInstanceExtra getItemInstanceExtra(Item item) { CompoundTag tag = item.getNamedTag(); if (tag != null) { - return new NativeItemInstanceExtra(item); + return new ItemInstanceExtra(item); } return null; } diff --git a/src/main/java/com/reider745/item/CustomItem.java b/src/main/java/com/reider745/item/CustomItem.java index a536bd5dc..2eb2a2a61 100644 --- a/src/main/java/com/reider745/item/CustomItem.java +++ b/src/main/java/com/reider745/item/CustomItem.java @@ -1,6 +1,6 @@ package com.reider745.item; -import cn.nukkit.item.Item; +import cn.nukkit.item.*; import cn.nukkit.item.food.Food; import cn.nukkit.item.food.FoodNormal; import com.reider745.InnerCoreServer; @@ -8,12 +8,10 @@ import com.reider745.block.CustomBlock; import com.reider745.hooks.item.ItemUtils; import com.reider745.item.ItemMethod.PropertiesNames; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import it.unimi.dsi.fastutil.ints.*; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.unlimited.IDRegistry; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.innercore.icstd.inventory.ItemInstanceExtra; import java.util.*; import java.util.function.Consumer; @@ -21,6 +19,8 @@ public class CustomItem { public static Int2ObjectMap items = new Int2ObjectOpenHashMap<>(); public static Int2IntMap foods = new Int2IntOpenHashMap(); + public static IntList armors = new IntArrayList(); + public static IntList projectile = new IntArrayList(); public static void init() { items.forEach((key, value) -> Item.list[key] = value.getClazz()); @@ -39,11 +39,38 @@ public static int getIdForText(String id) { return customItems.get(id); } + public static boolean isFood(int id) { + if(foods.containsKey(id)) return true; + return Item.get(id) instanceof ItemEdible; + } + + public static boolean isArmor(int id) { + if(armors.contains(id)) return true; + return Item.get(id).isArmor(); + } + + public static boolean isThrowable(int id) { + if(projectile.contains(id)) return true; + return Item.get(id) instanceof ProjectileItem; + } + + public static int getNutrition(int id) { + if (foods.containsKey(id)) { + return foods.get(id); + } + Item item = Item.get(id); + if(item instanceof ItemEdible) { + return Food.getByRelative(item).getRestoreFood(); + } + return 0; + } + + public static class ItemCreative { public int id, count, data; - public NativeItemInstanceExtra extra; + public ItemInstanceExtra extra; - public ItemCreative(int id, int count, int data, NativeItemInstanceExtra extra) { + public ItemCreative(int id, int count, int data, ItemInstanceExtra extra) { this.id = id; this.count = count; this.data = data; @@ -72,7 +99,7 @@ private static void addFirst(ArrayList list, T value) { list.addAll(newList); } - public static void checkAddedItem(int id, int count, int damage, NativeItemInstanceExtra extra) { + public static void checkAddedItem(int id, int count, int damage, ItemInstanceExtra extra) { if (!CustomItem.isCreativeItem(id, damage) && id >= IDRegistry.ITEM_ID_OFFSET) { CustomItem.addToCreativeGroup(CustomItem.TECHNICAL_GROUP, id); addFirst(CustomItem.creative, new ItemCreative(id, count, damage, extra)); @@ -135,11 +162,12 @@ public static CustomManager registerItemFood(String textId, int id, String name, return manager; } - public static void addCreative(int id, int count, int data, NativeItemInstanceExtra extra) { + public static void addCreative(int id, int count, int data, ItemInstanceExtra extra) { creative.add(new ItemCreative(id, count, data, extra)); } public static CustomManager registerThrowableItem(String nameId, int id, String name) { + projectile.add(id); return registerItem(nameId, id, name, CustomProjectileItem.class); } @@ -150,6 +178,7 @@ public static CustomManager registerArmorItem(String nameId, int id, String name manager.put(PropertiesNames.Armors.DEFENSE, defense); manager.put(PropertiesNames.MAX_DAMAGE, durability); manager.put(PropertiesNames.Armors.KNOCKBACK_RESIST, knockbackResist); + armors.add(id); return manager; } diff --git a/src/main/java/com/reider745/item/CustomItemClass.java b/src/main/java/com/reider745/item/CustomItemClass.java index 45f4ba7db..02d0aac47 100644 --- a/src/main/java/com/reider745/item/CustomItemClass.java +++ b/src/main/java/com/reider745/item/CustomItemClass.java @@ -11,6 +11,7 @@ import com.zhekasmirnov.innercore.api.NativeCallback; import com.zhekasmirnov.innercore.api.constants.EnchantType; import com.zhekasmirnov.innercore.api.runtime.Callback; +import org.innercore.icstd.event.Events; public class CustomItemClass extends Item { protected CustomManager parameters; @@ -103,7 +104,7 @@ public int getEnchantAbility() { @Override public boolean onClickAir(Player player, Vector3 directionVector) { NativeCallback.onItemUsedNoTarget(EntityMethod.getIdForEntity(player)); - return Callback.count("ItemUseNoTarget") > 0 || canRelease(); + return Events.count("ItemUseNoTarget") > 0 || canRelease(); } @Override diff --git a/src/main/java/com/reider745/item/ItemMethod.java b/src/main/java/com/reider745/item/ItemMethod.java index 13fd229a2..2e31f137a 100644 --- a/src/main/java/com/reider745/item/ItemMethod.java +++ b/src/main/java/com/reider745/item/ItemMethod.java @@ -1,19 +1,21 @@ package com.reider745.item; -import cn.nukkit.item.Item; -import cn.nukkit.item.ItemBlock; +import cn.nukkit.inventory.PlayerOffhandInventory; +import cn.nukkit.item.*; import com.reider745.InnerCoreServer; import com.reider745.api.CustomManager; import com.reider745.block.CustomBlock; import com.reider745.hooks.item.ItemUtils; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.adaptedscript.AdaptedScriptAPI.IDRegistry; import com.zhekasmirnov.innercore.api.runtime.other.NameTranslation; import it.unimi.dsi.fastutil.ints.Int2BooleanMap; import it.unimi.dsi.fastutil.ints.Int2BooleanOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import oshi.software.os.unix.freebsd.FreeBsdOSThread; import java.util.ArrayList; import java.util.HashMap; @@ -73,6 +75,11 @@ public static void setCreativeCategory(int id, int val) { CREATIVE_CATEGORY.put(id, val); } + public static int getCategory(int id) { + // TODO: not support vanilla + return CREATIVE_CATEGORY.getOrDefault(id, -1); + } + public static int getCreativeCategory(int id) { CustomManager manager = getCustomManager(id); if (manager != null) @@ -91,7 +98,7 @@ public static int getMaxStackForId(int id, int data) { return 64; } - public static String getNameForId(int id, int data, NativeItemInstanceExtra extra) { + public static String getNameForId(int id, int data, ItemInstanceExtra extra) { CustomManager manager = CustomBlock.getBlockManager(id); if (manager != null) return NameTranslation.translate(CustomBlock.getVariants(manager).get(data)); @@ -145,10 +152,28 @@ public static void setHandEquipped(CustomManager manager, boolean val) { manager.put(PropertiesNames.HAND_EQUIPPED, val); } + public static boolean isAllowedInOffhand(int id) { + Item item = Item.get(id); + return item instanceof ItemArrow || item instanceof ItemMap || item instanceof ItemShield || item instanceof ItemTotem; + } + + public static void setAllowedInOffhand(int id, boolean value) { + // JIC if something changed, check PlayerOffhandInventory.OFFHAND_ITEMS + } + public static void setLiquidClip(CustomManager manager, boolean val) { manager.put(PropertiesNames.LIQUID_CLIP, val); } + public static boolean isLiquidClip(int id) { + CustomManager manager = CustomItem.getItemManager(id); + if(manager != null) return manager.get(PropertiesNames.LIQUID_CLIP); + Item item = Item.get(id); + + // TODO: not all vanilla items + return item instanceof ItemBucket; + } + public static void setMaxUseDuration(CustomManager manager, int val) { manager.put(PropertiesNames.MAX_USE_DURATION, val); } @@ -177,7 +202,10 @@ public static boolean canShouldDespawn(int id){ return despawn.getOrDefault(id, true); } + private static final Int2BooleanMap fireResistance = new Int2BooleanOpenHashMap(); + public static void setFireResistant(CustomManager manager, boolean resist) { + fireResistance.put((int) manager.get(PropertiesNames.ID, 0), resist); if (resist || (manager.get(PropertiesNames.ID, 0) >= Item.NETHERITE_INGOT && manager.get(PropertiesNames.ID, 0) <= Item.NETHERITE_SCRAP)) { manager.put(PropertiesNames.FIRE_RESISTANT, resist); @@ -186,6 +214,10 @@ public static void setFireResistant(CustomManager manager, boolean resist) { } } + public static boolean isFireResistant(int id) { + return id >= Item.NETHERITE_INGOT && id <= Item.NETHERITE_SCRAP || fireResistance.get(id); + } + public static HashMap explodable = new HashMap<>(); public static void setExplodable(int id, boolean value) { @@ -196,6 +228,11 @@ public static boolean canExplodable(int id){ return explodable.getOrDefault(id, true); } + public static boolean isArmorDamageable(int id) { + Item item = Item.get(id); + return !item.isUnbreakable(); + } + public static int getArmorValue(int id) { Item item = Item.get(id); if(item != null) @@ -203,6 +240,18 @@ public static int getArmorValue(int id) { return 0; } + public static int getEnchantSlot(int id) { + Item item = Item.get(id); + + //TODO: probably not a correct implementation + + if(item.isHelmet()) return 0; + if(item.isChestplate()) return 1; + if(item.isLeggings()) return 2; + if(item.isBoots()) return 3; + return 0; + } + public static void addRepairItemId(CustomManager manager, int id) { ArrayList repairs = manager.get(PropertiesNames.REPAIRS); if (repairs == null) { diff --git a/src/main/java/com/reider745/item/NukkitIdConverter.java b/src/main/java/com/reider745/item/NukkitIdConverter.java index 5f1990d7e..1fb120b7b 100644 --- a/src/main/java/com/reider745/item/NukkitIdConverter.java +++ b/src/main/java/com/reider745/item/NukkitIdConverter.java @@ -1,6 +1,7 @@ package com.reider745.item; import com.zhekasmirnov.innercore.api.NativeItemInstance; +import org.innercore.icstd.inventory.ItemInstance; import java.util.*; @@ -118,9 +119,17 @@ public static void convert(NativeItemInstance instance) { } } + public static void convert(ItemInstance instance) { + final EntryItem target = getInnerCoreForNukkit(instance.id, instance.data); + if (target != null) { + instance.id = target.id; + instance.data = target.data; + } + } + public static EntryItem getInnerCoreForNukkit(int id, int data) { EntryItem entryItem = new EntryItem(id, data); - return nukkitToInnerCore.containsKey(entryItem) ? nukkitToInnerCore.get(entryItem) : entryItem; + return nukkitToInnerCore.getOrDefault(entryItem, entryItem); } public static EntryItem getNukkitForInnerCore(int id, int data) { diff --git a/src/main/java/com/reider745/world/BlockSourceMethods.java b/src/main/java/com/reider745/world/BlockSourceMethods.java deleted file mode 100644 index 9f3cbe1f6..000000000 --- a/src/main/java/com/reider745/world/BlockSourceMethods.java +++ /dev/null @@ -1,352 +0,0 @@ -package com.reider745.world; - -import cn.nukkit.Player; -import cn.nukkit.Server; -import cn.nukkit.block.Block; -import cn.nukkit.block.BlockID; -import cn.nukkit.block.BlockKelp; -import cn.nukkit.blockentity.BlockEntity; -import cn.nukkit.entity.Entity; -import cn.nukkit.entity.item.EntityItem; -import cn.nukkit.item.Item; -import cn.nukkit.level.Explosion; -import cn.nukkit.level.Level; -import cn.nukkit.level.Position; -import cn.nukkit.level.particle.DestroyBlockParticle; -import cn.nukkit.math.BlockVector3; -import cn.nukkit.math.SimpleAxisAlignedBB; -import cn.nukkit.math.Vector3; -import cn.nukkit.nbt.tag.CompoundTag; -import cn.nukkit.network.protocol.PlaySoundPacket; - -import com.reider745.InnerCoreServer; -import com.reider745.api.ReflectHelper; -import com.reider745.block.BlockStateStorage; -import com.reider745.entity.EntityMethod; -import com.reider745.hooks.item.ItemUtils; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -public class BlockSourceMethods { - - private static void defDestroy(Level level, Block target) { - BlockEntity blockEntity = level.getBlockEntity(target); - if (blockEntity != null) { - blockEntity.onBreak(); - blockEntity.close(); - level.updateComparatorOutputLevel(target); - } - } - - public static Level getLevelForDimension(int dimension) { - return FakeDimensions.getLevelForFakeId(dimension); - } - - public static Level getLevelForName(String name) { - return Server.getInstance().getLevelByName(name); - } - - public static Level getDefaultLevel() { - return Server.getInstance().getDefaultLevel(); - } - - public static void destroyBlock(Level level, int x, int y, int z, boolean drop, int updateType, - boolean destroyParticles) { - boolean update = updateType == 3; - Block block = level.getBlock(x, y, z); - if (destroyParticles) { - Map players = level.getChunkPlayers(x >> 4, z >> 4); - level.addParticle(new DestroyBlockParticle(block.add(0.5), block), players.values()); - } - if (drop && block.getId() < 8000) { - Item[] drops = block.getDrops(Item.get(0)); - Vector3 pos = new Vector3(x, y, z); - for (Item item : drops) - level.dropItem(pos, item); - } - defDestroy(level, block); - level.setBlock(x, y, z, Block.get(0), true, update); - } - - public static int getBlockId(Level level, int x, int y, int z) { - if(y >= level.getMinBlockY() && y <= level.getMaxBlockY()) - return level.getBlock(x, y, z).getId(); - return 0; - } - - public static boolean isChunkLoaded(Level level, int x, int z) { - return level.isChunkLoaded(x, z); - } - - public static long spawnDroppedItem(Level level, float x, float y, float z, int id, int count, int data, NativeItemInstanceExtra extra) { - EntityItem item = level.dropAndGetItem(new Vector3(x, y, z), ItemUtils.get(id, count, data, extra)); - if (item == null) - return 0; - return item.getId(); - } - - public static boolean canSeeSky(Level level, int x, int y, int z) { - y++; - while (y < level.getMaxBlockY()) { - if(level.getBlockIdAt(x, y, z) != 0) return false; - y++; - } - return true; - } - - public static int getBiome(Level level, int x, int y, int z) { - return level.getBiomeId(x, z); - } - - public static float getBiomeTemperatureAt(Level level, int x, int y, int z) { - return 0; - // return Biome.getBiome(level.getBiomeId(x, z)).get - } - - public static float getBiomeDownfallAt(Level level, int x, int y, int z) { - return 0; - } - - public static int getBrightness(Level level, int x, int y, int z) { - int time = level.getTime(); - if (!(time >= Level.TIME_NIGHT && time < Level.TIME_SUNRISE) && level.canBlockSeeSky(new Vector3(x, y, z))) - return 15; - return level.getBlockLightAt(x, y, z); - } - - public static int getBlockData(Level level, int x, int y, int z) { - if(y >= level.getMinBlockY() && y <= level.getMaxBlockY()) - return level.getBlockDataAt(x, y, z); - return 0; - } - - public static Block getBlockIdDataAndState(Level level, int x, int y, int z) { - return level.getBlock(x, y, z); - } - - public static Block getExtraBlockIdDataAndState(Level level, int x, int y, int z) { - return level.getBlock(x, y, z); - } - - public static void setBlock(Level level, int x, int y, int z, int id, int data, boolean allowUpdate, - int updateType) { - Block block = level.getBlock(x, y, z); - defDestroy(level, block); - try{ - level.setBlock(x, y, z, Block.get(id, data), true, allowUpdate); - }catch (Exception e){} - } - - public static void setBlockByRuntimeId(Level level, int x, int y, int z, int runtimeId, boolean allowUpdate, - int updateType) { - int[] block = BlockStateStorage.getBlockForRuntimeId(runtimeId); - setBlock(level, x, y, z, block[0], block[1], allowUpdate, updateType); - } - - public static void setExtraBlock(Level level, int x, int y, int z, int id, int data, boolean allowUpdate, - int updateType) { - level.setBlockExtraDataAt(x, y, z, id, data); - } - - public static void setExtraBlockByRuntimeId(Level level, int x, int y, int z, int runtimeId, boolean allowUpdate, - int updateType) { - int[] block = BlockStateStorage.getBlockForRuntimeId(runtimeId); - setExtraBlock(level, x, y, z, block[0], block[1], allowUpdate, updateType); - } - - public static int getGrassColor(Level level, int x, int y, int z) { - return 0; - } - - private static final Int2ObjectMap TILES_TYPES = new Int2ObjectOpenHashMap<>(); - - static { - TILES_TYPES.put(BlockID.CHEST, BlockEntity.CHEST); - TILES_TYPES.put(BlockID.TRAPPED_CHEST, BlockEntity.CHEST); - TILES_TYPES.put(BlockID.ENDER_CHEST, BlockEntity.ENDER_CHEST); - TILES_TYPES.put(BlockID.FURNACE, BlockEntity.FURNACE); - TILES_TYPES.put(BlockID.BLAST_FURNACE, BlockEntity.BLAST_FURNACE); - TILES_TYPES.put(BlockID.SMOKER, BlockEntity.SMOKER); - TILES_TYPES.put(BlockID.MONSTER_SPAWNER, BlockEntity.MOB_SPAWNER); - TILES_TYPES.put(BlockID.ENCHANT_TABLE, BlockEntity.ENCHANT_TABLE); - TILES_TYPES.put(BlockID.ITEM_FRAME_BLOCK, BlockEntity.ITEM_FRAME); - TILES_TYPES.put(BlockID.HOPPER_BLOCK, BlockEntity.HOPPER); - TILES_TYPES.put(BlockID.DROPPER, BlockEntity.DROPPER); - } - - public static BlockEntity getBlockEntity(Level level, int x, int y, int z) { - BlockEntity entity = level.getBlockEntity(new BlockVector3(x, y, z)); - - // TODO: Некоторые моды просто не знают, что блок может быть, а BlockEntity может и не быть(например StorageInterface) - if(entity == null){ - final String type = TILES_TYPES.get(getBlockId(level, x, y, z)); - if(type != null){ - entity = BlockEntity.createBlockEntity(type, new Position(x, y, z).setLevel(level), new CompoundTag(type) - .putString("id", type) - .putInt("x", x) - .putInt("y", y) - .putInt("z", z)); - } - } - - return entity; - } - - public static int getDimension(Level level) { - return FakeDimensions.getFakeIdForLevel(level); - } - - public static void setBiome(Level level, int chunkX, int chunkZ, int id) { - level.setBiomeId(chunkX, chunkZ, (byte) id); - } - - public static void setRespawnCoords(Level level, int x, int y, int z) { - level.setSpawnLocation(new Vector3(x, y, z)); - } - - public static int getChunkState(Level level, int chunkX, int chunkZ) { - InnerCoreServer.useNotCurrentSupport("BlockSourceMethods.getChunkState(level, chunkX, chunkZ)"); - return 0; - } - - public static void addToTickingQueue(Level level, int x, int y, int z, int runtimeId /* = -1 */, int delay, - int unknown /* = 0 */) { - InnerCoreServer.useNotCurrentSupport( - "BlockSourceMethods.addToTickingQueue(level, x, y, z, runtimeId, delay, unknown)"); - } - - public static void explode(Level level, float x, float y, float z, float power, boolean fire) { - final Block block = level.getBlock((int) x, (int) y, (int) z); - final Position position = new Position(x, y, z); - position.setLevel(level); - final Explosion explosion = new Explosion(position, power, block); - explosion.explodeA(); - explosion.explodeB(); - } - - public static long clip(Level level, float x1, float y1, float z1, float x2, float y2, float z2, int mode, - float[] joutput) { - InnerCoreServer.useNotCurrentSupport("BlockSourceMethods.clip(level, x1, y1, z1, x2, y2, z2, mode, joutput)"); - return 0; - } - - private static Entity[] fetchEntitiesInAABB(Level level, float x1, float y1, float z1, float x2, float y2, - float z2) { - return level.getCollidingEntities( - new SimpleAxisAlignedBB((double) x1, (double) y1, (double) z1, (double) x2, (double) y2, (double) z2)); - } - - public static long[] fetchEntitiesInAABB(Level level, float x1, float y1, float z1, float x2, float y2, float z2, - int backCompEntityType, boolean blacklist) { - return Arrays.stream(fetchEntitiesInAABB(level, x1, y1, z1, x2, y2, z2)) - .filter(entity -> { - if (backCompEntityType == 256) { - return true; - } - int entityType = EntityMethod.getEntityTypeDirect(entity); - return !blacklist ? entityType == backCompEntityType : entityType != backCompEntityType; - }) - .mapMultiToLong((entity, consumer) -> consumer.accept(EntityMethod.getIdForEntity(entity))) - .toArray(); - } - - public static long[] fetchEntitiesOfTypeInAABB(Level level, float x1, float y1, float z1, float x2, float y2, - float z2, String namespace, String name) { - // TODO: Quite BETTER name to identifier conversion! - return Arrays.stream(fetchEntitiesInAABB(level, x1, y1, z1, x2, y2, z2)) - .filter(entity -> entity.getName().equalsIgnoreCase(name)) - .mapMultiToLong((entity, consumer) -> consumer.accept(EntityMethod.getIdForEntity(entity))) - .toArray(); - } - - public static long spawnEntity(Level level, int type, float x, float y, float z) { - Entity entity = Entity.createEntity(type, new Position(x, y, z, level)); - if (entity != null) { - entity.spawnToAll(); - return EntityMethod.getIdForEntity(entity); - } - return -1; - } - - public static long spawnNamespacedEntity(Level level, float x, float y, float z, String str1, String str2, - String str3) { - if (str2 == null) { - return -1; - } - // TODO: Quite BETTER name to identifier conversion! - Entity entity = Entity.createEntity(str2.substring(0, 1).toUpperCase() + str2.substring(1).replace("_", ""), - new Position(x, y, z, level)); - if (entity != null) { - entity.spawnToAll(); - return EntityMethod.getIdForEntity(entity); - } - return -1; - } - - public static long spawnExpOrbs(Level level, float x, float y, float z, int amount) { - if(amount > 0) { - final Position source = new Position(x, y, z, level); - final CompoundTag nbt = Entity.getDefaultNBT(source, new Vector3(0, 0, 0)); - - nbt.putShort("Value", amount); - nbt.putShort("PickupDelay", 10); - - final Entity entity = Entity.createEntity("XpOrb", level.getChunk(source.getChunkX(), source.getChunkZ()), nbt); - if (entity != null) { - entity.spawnToAll(); - return EntityMethod.getIdForEntity(entity); - } - } - - return -1; - } - - public static void playSound(Level level, String sound, int x, int y, int z, float volume, float pitch, Player[] players) { - PlaySoundPacket packet = new PlaySoundPacket(); - packet.name = sound; - packet.volume = volume; - packet.pitch = pitch; - packet.x = x; - packet.y = y; - packet.z = z; - - if (players == null || players.length == 0) { - level.addChunkPacket(x >> 4, z >> 4, packet); - } else { - Server.broadcastPacket(players, packet); - } - } - - public static float getRainLevel(Level level) { - return level.isRaining() ? 1 : 0; - } - - public static void setRainLevel(Level level, float rainLevel) { - level.setRaining(rainLevel != 0); - } - - public static float getLightningLevel(Level level) { - return level.isThundering() ? 1 : 0; - } - - public static void setLightningLevel(Level level, float lightningLevel) { - level.setThundering(lightningLevel != 0); - } - - private static final boolean[] randomTickBlocks = ReflectHelper.getField(Level.class, "randomTickBlocks"); - - public static void randomTick(Level level, int x, int y, int z) { - final Block block = level.getBlock(x, y, z); - - assert randomTickBlocks != null; - - if(randomTickBlocks[block.getId()]) { - block.onUpdate(Level.BLOCK_UPDATE_RANDOM); - } - } -} diff --git a/src/main/java/com/reider745/world/FakeDimensions.java b/src/main/java/com/reider745/world/FakeDimensions.java index eb18cd91d..348be7c6c 100644 --- a/src/main/java/com/reider745/world/FakeDimensions.java +++ b/src/main/java/com/reider745/world/FakeDimensions.java @@ -65,4 +65,10 @@ public static Level getLevelForFakeId(int fakeId){ .filter(level -> level.getDimension() == fakeId) .findFirst().orElse(null); } + + public static void unload(Level level) { + if(fakeIdForLevelId.containsKey(level.getId())) { + levelForFakeId.remove(fakeIdForLevelId.remove(level.getId())); + } + } } \ No newline at end of file diff --git a/src/main/java/com/reider745/world/GenerationUtilsMethods.java b/src/main/java/com/reider745/world/GenerationUtilsMethods.java index 015de8c9f..9789a3f90 100644 --- a/src/main/java/com/reider745/world/GenerationUtilsMethods.java +++ b/src/main/java/com/reider745/world/GenerationUtilsMethods.java @@ -7,10 +7,9 @@ import cn.nukkit.math.MathHelper; import cn.nukkit.math.NukkitMath; import cn.nukkit.math.NukkitRandom; -import cn.nukkit.math.Vector3; import com.reider745.api.CallbackHelper; import com.reider745.world.dimensions.NoiseOctaveMethods; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; +import org.innercore.icstd.region.BlockSource; import java.util.ArrayList; @@ -24,7 +23,7 @@ public static boolean isTransparentBlock(int id){ } public static boolean canSeeSky(int x, int y, int z){ - return NativeBlockSource.getCurrentWorldGenRegion().canSeeSky(x, y, z); + return BlockSource.getCurrentWorldGenRegion().canSeeSky(x, y, z); } private static final ArrayList blackListSurface = new ArrayList<>(); diff --git a/src/main/java/com/reider745/world/dimensions/MonoBiomeTerrainMethods.java b/src/main/java/com/reider745/world/dimensions/MonoBiomeTerrainMethods.java index cb1dd5db6..d99e6a9c6 100644 --- a/src/main/java/com/reider745/world/dimensions/MonoBiomeTerrainMethods.java +++ b/src/main/java/com/reider745/world/dimensions/MonoBiomeTerrainMethods.java @@ -3,7 +3,7 @@ import cn.nukkit.level.Level; import com.reider745.api.pointers.PointersStorage; import com.reider745.api.pointers.pointer_gen.PointerGenFastest; -import com.reider745.world.BlockSourceMethods; +import com.reider745.world.FakeDimensions; import java.util.ArrayList; @@ -23,7 +23,7 @@ public void generateChunk(DimensionsMethods.CustomDimensionGenerator generator, public void populateChunk(DimensionsMethods.CustomDimensionGenerator generator, int chunkX, int chunkZ) { final Noise.Data data = new Noise.Data(); data.prepare_noise_data((int) generator.seed); - final Level level = BlockSourceMethods.getLevelForDimension(generator.getId()); + final Level level = FakeDimensions.getLevelForFakeId(generator.getId()); for(final TerrainLayerMethods.TerrainLayerDescription layer : layers) layer.rebuildThresholds(); diff --git a/src/main/java/com/zhekasmirnov/apparatus/Apparatus.java b/src/main/java/com/zhekasmirnov/apparatus/Apparatus.java index 2724bc496..059b682e1 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/Apparatus.java +++ b/src/main/java/com/zhekasmirnov/apparatus/Apparatus.java @@ -1,12 +1,14 @@ package com.zhekasmirnov.apparatus; import com.reider745.InnerCoreServer; -import com.zhekasmirnov.apparatus.api.container.ItemContainer; +import org.innercore.icstd.inventory.ItemContainer; import com.zhekasmirnov.apparatus.api.player.NetworkPlayerRegistry; import com.zhekasmirnov.apparatus.multiplayer.mod.IdConversionMap; import com.zhekasmirnov.apparatus.multiplayer.mod.MultiplayerModList; import com.zhekasmirnov.apparatus.multiplayer.mod.MultiplayerPackVersionChecker; import com.zhekasmirnov.apparatus.multiplayer.util.entity.NetworkEntity; +import com.zhekasmirnov.innercore.api.runtime.Updatable; +import org.innercore.icstd.scene.GameObjectManager; public class Apparatus { static { @@ -16,6 +18,8 @@ public class Apparatus { MultiplayerModList.loadClass(); IdConversionMap.loadClass(); ItemContainer.loadClass(); + GameObjectManager.loadClass(); + Updatable.loadClass(); } public static void loadClasses() { diff --git a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/UserDialog.java b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/UserDialog.java index 5040fb561..40cf2679b 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/UserDialog.java +++ b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/UserDialog.java @@ -1,13 +1,74 @@ package com.zhekasmirnov.apparatus.adapter.innercore; -import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.innercore.api.NativeAPI; +import com.zhekasmirnov.innercore.api.log.DialogHelper; +import com.zhekasmirnov.innercore.api.log.ICLog; +import com.zhekasmirnov.innercore.api.runtime.other.PrintStacking; public class UserDialog { - public static void dialog(String str, String s, Throwable err, boolean b) { - Logger.error(str, s, err); + public static void toast(String message) { + PrintStacking.print(message); + if (message.length() < 8192) + ICLog.d("TOAST", message); } - public static void insignificantError(String s, Throwable th, boolean b) { - Logger.error(s, th); + public static void message(String message) { + NativeAPI.clientMessage(message); + } + + public static void tip(String message) { + NativeAPI.tipMessage(message); + } + + public static void dialog(String title, String message, Throwable error, boolean showLog, boolean isForced) { + if (isForced) { + DialogHelper.openFormattedDialog(message + (error != null ? "\n\nSTACKTRACE:\n" + DialogHelper.getFormattedStackTrace(error) : "") + (showLog ? "\n\nLOG:\n" + DialogHelper.getFormattedLog() : ""), title); + } else { + DialogHelper.openFormattedDialog(message + + (error != null ? "\n\nSTACKTRACE:\n" + DialogHelper.getFormattedStackTrace(error) : "") + + (showLog ? "\n\nLOG:\n" + DialogHelper.getFormattedLog() : ""), + title, 3, () -> insignificantError(title + ": " + message, error, true, false)); + } + } + + public static void dialog(String title, String message, Throwable error, boolean showLog) { + dialog(title, message, error, showLog, false); + } + + public static void dialog(String title, String message) { + dialog(title, message, null, false); + } + + public static void dialog(String message) { + dialog("message", message); + } + + + private static class DecisionStatus { + private boolean result = false; + } + + public static boolean awaitDecision(String title, String text, String positiveLabel, String negativeLabel) { + return false; + } + + public static void insignificantError(String message, Throwable err, boolean showToast, boolean showTrace) { + if (message.length() < 8192) + ICLog.e("ERROR", message, err); + if (showToast) { + if (err != null) { + toast(message + ": " + err.getMessage() + (showTrace ? ", stacktrace: \n" + ICLog.getStackTrace(err) : ", see log for details ")); + } else { + toast(message); + } + } + } + + public static void insignificantError(String message, Throwable err, boolean showToast) { + insignificantError(message, err, showToast, false); + } + + public static void insignificantError(String message, Throwable err) { + insignificantError(message, err, true); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockBreakResult.java b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockBreakResult.java index 8445dc022..e8c10a89e 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockBreakResult.java +++ b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockBreakResult.java @@ -1,8 +1,8 @@ package com.zhekasmirnov.apparatus.adapter.innercore.game.block; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; import com.zhekasmirnov.apparatus.util.Java8BackComp; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import org.innercore.icstd.inventory.ItemInstance; import org.mozilla.javascript.ScriptableObject; import java.util.ArrayList; @@ -10,10 +10,10 @@ import java.util.stream.Collectors; public class BlockBreakResult { - private final List items = new ArrayList<>(); + private final List items = new ArrayList<>(); private int experience; - public List getItems() { + public List getItems() { return items; } @@ -33,7 +33,7 @@ public void addExperience(int experience) { public ScriptableObject asScriptable() { ScriptableObject result = ScriptableObjectHelper.createEmpty(); result.put("experience", result, experience); - result.put("items", result, ScriptableObjectHelper.createArray(Java8BackComp.stream(items).map(ItemStack::asScriptable).collect(Collectors.toList()))); + result.put("items", result, ScriptableObjectHelper.createArray(Java8BackComp.stream(items).collect(Collectors.toList()))); return result; } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockState.java b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockState.java index aaa0a7712..98318c185 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockState.java +++ b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/block/BlockState.java @@ -1,286 +1,34 @@ package com.zhekasmirnov.apparatus.adapter.innercore.game.block; import cn.nukkit.block.Block; -import cn.nukkit.block.BlockMeta; -import cn.nukkit.level.GlobalBlockPalette; -import com.reider745.InnerCoreServer; -import com.reider745.block.BlockStateStorage; -import com.reider745.hooks.block.BlockPalette; -import com.zhekasmirnov.apparatus.minecraft.enums.EnumsContainer; -import com.zhekasmirnov.apparatus.minecraft.enums.GameEnums; -import com.zhekasmirnov.apparatus.minecraft.version.MinecraftVersions; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.annotations.JSStaticFunction; -import java.util.HashMap; import java.util.Map; -public class BlockState { - private static final HashMap ALL_STATES = new HashMap<>(); - private static final HashMap VANILLA_STATES = new HashMap<>(); - private static final HashMap CAPACITY_STATES = new HashMap<>(); - - static { - final EnumsContainer.Scope scope = GameEnums.getSingleton().getOrAddContainerForVersion(MinecraftVersions.getCurrent()) - .getScope("block_states"); - - for(String name : scope.getAllEnumNames()) { - final int id = ((Number) scope.getEnum(name)).intValue(); - - ALL_STATES.put(name, id); - VANILLA_STATES.put(name, id); - } - } - - public static int STATE_ID = 114; - - public static int registerBlockState(String name, int capacity) { - if(VANILLA_STATES.containsKey(name)) - throw new RuntimeException("You can't create a state with a vanilla name"); - - if(capacity <= 0) - throw new RuntimeException("capacity state <= 0"); - - final int id = ALL_STATES.computeIfAbsent(name, n -> STATE_ID++); - final Integer maxCapacity = CAPACITY_STATES.get(id); - - if(maxCapacity == null) { - registerBlockState(name, id, capacity); - CAPACITY_STATES.put(id, capacity); - } else if(maxCapacity < capacity) { - setBlockStateCapacity(id, capacity); - } - - return id; - } - - @JSStaticFunction - public static int getIdByName(String name) { - return ALL_STATES.getOrDefault(name, -1); - } - - @JSStaticFunction - public static String getNameById(int id) { - for(String name : ALL_STATES.keySet()) - if(ALL_STATES.get(name) == id) - return name; - return null; - } - - @JSStaticFunction - public static int getBlockStateCapacity(int id) { - return CAPACITY_STATES.getOrDefault(id, 0); - } - - public final int id; - public final int data; - public final int runtimeId; - - private int[] rawStates = null; - private Map states = null; - private Map namedStates = null; - +// this is a stub class for DungeonUtility, it MUST NOT be used in code +@Deprecated +public class BlockState extends org.innercore.icstd.region.BlockState { public BlockState(int id, int data) { - this.id = id; - this.data = data; - this.runtimeId = -1; - } - - public BlockState(Block idDataAndState) { - /*long idData = idDataAndState & (long) 0xFFFFFFFF; - this.id = (int) (idData >> (long) 16) & 0xFFFF; - this.data = (int) (idData & (long) 0xFFFF); - this.runtimeId = (int) (idDataAndState >> (long) 32);*/ - this.id = idDataAndState.getId(); - if(idDataAndState instanceof BlockMeta) { - BlockMeta meta = (BlockMeta) idDataAndState; - this.data = meta.getDamage(); - }else - this.data = 0; - this.runtimeId = GlobalBlockPalette.getOrCreateRuntimeId(InnerCoreServer.PROTOCOL, id, data); + super(id, data); } public BlockState(int id, Map map) { - this.id = id; - - rawStates = new int[map.size() * 2]; - int index = 0; - for (Map.Entry entry : map.entrySet()) { - Object key0 = entry.getKey(); - int key = -1; - if (key0 instanceof Integer) { - key = (int) key0; - } else if (key0 instanceof CharSequence) { - key = getIdByName(key0.toString()); - } - if (key == -1) { - continue; - } - rawStates[index++] = key; - rawStates[index++] = entry.getValue(); - } - - this.runtimeId = runtimeIdByStates(id, rawStates); - this.data = getDataFromRuntimeId(runtimeId); + super(id, map); } - @SuppressWarnings("unused") - private BlockState(int id, int[] rawStates) { - this.id = id; - this.rawStates = rawStates; - this.runtimeId = runtimeIdByStates(id, rawStates); - this.data = getDataFromRuntimeId(runtimeId); - } - - private static Map scriptableToStateMap(Scriptable scriptable) { - Map result = new HashMap<>(); - for (Object key0 : scriptable.getIds()) { - String key = key0.toString(); - - int state = getIdByName(key); - if (state == -1) { - try { - state = Integer.parseInt(key); - } catch (NumberFormatException e) { - continue; - } - } - - Object value = scriptable.get(key, scriptable); - result.put(state, value instanceof Number ? ((Number) value).intValue() : 0); - } - return result; + public BlockState(long ptr, int refCnt, Object stub) { + super(ptr, refCnt, stub); } public BlockState(int id, Scriptable scriptable) { - this(id, scriptableToStateMap(scriptable)); - } - - public int getId() { - return id; - } - - public int getData() { - return data; - } - - public int getRuntimeId() { - return runtimeId; + super(id, scriptable); } - public boolean isValidState() { - return runtimeId != -1; + public BlockState(long idDataAndState) { + super(idDataAndState, null); } - public int getState(int state) { - return runtimeId > 0 ? getStateFromId(runtimeId, state) : -1; - } - - public boolean hasState(int state) { - return getState(state) != -1; - } - - public BlockState addState(int state, int value) { - Map mStates = new HashMap<>(getStates()); - mStates.put(state, value); - return new BlockState(id, mStates); - } - - public BlockState addStatesMap(Map states) { - Map mStates = new HashMap<>(getStates()); - mStates.putAll(states); - return new BlockState(id, mStates); - } - - public BlockState addStates(Scriptable states) { - return addStatesMap(scriptableToStateMap(states)); - } - - public Map getStates() { - if (states == null) { - states = new HashMap<>(); - if (runtimeId > 0) { - rawStates = rawStates != null ? rawStates : getAllStatesFromId(runtimeId); - for (int i = 0; i < rawStates.length / 2; i++) { - states.put(rawStates[i * 2], rawStates[i * 2 + 1]); - } - } - } - return states; - } - - public Map getNamedStates() { - if (namedStates == null) { - namedStates = new HashMap<>(); - Map states = getStates(); - for (Map.Entry entry : states.entrySet()) { - namedStates.put(getNameById(entry.getKey()), entry.getValue()); - } - } - return namedStates; - } - - public ScriptableObject getStatesScriptable() { - Map states = getStates(); - ScriptableObject result = ScriptableObjectHelper.createEmpty(); - for (Map.Entry entry : states.entrySet()) { - result.put(entry.getKey().toString(), result, entry.getValue()); - } - return result; - } - - public ScriptableObject getNamedStatesScriptable() { - Map states = getNamedStates(); - ScriptableObject result = ScriptableObjectHelper.createEmpty(); - for (Map.Entry entry : states.entrySet()) { - result.put(entry.getKey(), result, entry.getValue()); - } - return result; - } - - @Override - public String toString() { - return "BlockState{id=" + id + ", data=" + data + ", runtimeId=" + runtimeId + ", states=" + getNamedStates() + "}"; - } - - @Override - public boolean equals(Object object) { - if (this == object) return true; - if (object == null || getClass() != object.getClass()) return false; - - BlockState that = (BlockState) object; - - if (id != that.id) return false; - if (data != that.data) return false; - if (runtimeId != that.runtimeId) return false; - return getStates().equals(that.getStates()); - } - - private static void registerBlockState(String name, int id, int capacity) { - BlockPalette.registerBlockState(name, id, capacity); - } - - private static void setBlockStateCapacity(int id, int capacity) { - BlockPalette.setCapacityForId(id, capacity); - } - - - private static int getStateFromId(int runtimeId, int state){ - return BlockStateStorage.getStateFromId(runtimeId, state); - } - private static int[] getAllStatesFromId(int runtimeId){ - return BlockStateStorage.getAllStatesFromId(runtimeId); - } - public static int runtimeIdByStates(int id, int[] statesAndValues){ - return BlockStateStorage.runtimeIdByStates(id, statesAndValues); - } - public static int getIdFromRuntimeId(int runtimeId){ - InnerCoreServer.useIncomprehensibleMethod("getIdFromRuntimeId"); - return 0; - } - public static int getDataFromRuntimeId(int runtimeId){ - return BlockStateStorage.getDataForRuntimeId(runtimeId); + public BlockState(Block block) { + super(block); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/EntityActor.java b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/EntityActor.java index 757bff728..2cd14396c 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/EntityActor.java +++ b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/EntityActor.java @@ -1,8 +1,8 @@ package com.zhekasmirnov.apparatus.adapter.innercore.game.entity; import com.reider745.entity.EntityMethod; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; import com.zhekasmirnov.innercore.api.NativeAPI; +import org.innercore.icstd.inventory.ItemInstance; public class EntityActor { private final long uid; @@ -31,19 +31,19 @@ public int getMaxHealth() { // inventory - public ItemStack getCarriedItem() { - return ItemStack.fromPtr(EntityMethod.getEntityCarriedItem(uid)); + public ItemInstance getCarriedItem() { + return ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(uid)); } - public ItemStack getOffhandItem() { - return ItemStack.fromPtr(EntityMethod.getEntityOffhandItem(uid)); + public ItemInstance getOffhandItem() { + return ItemInstance.makeFrom(EntityMethod.getEntityOffhandItem(uid)); } - public ItemStack getArmorSlot(int slot) { - return ItemStack.fromPtr(EntityMethod.getEntityArmor(uid, slot)); + public ItemInstance getArmorSlot(int slot) { + return ItemInstance.makeFrom(EntityMethod.getEntityArmor(uid, slot)); } - public void setArmorSlot(int slot, ItemStack stack) { + public void setArmorSlot(int slot, ItemInstance stack) { NativeAPI.setEntityArmor(uid, slot, stack.id, stack.count, stack.data, stack.extra); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/StaticEntity.java b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/StaticEntity.java index 5f71550f7..f0d446555 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/StaticEntity.java +++ b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/entity/StaticEntity.java @@ -1,11 +1,12 @@ package com.zhekasmirnov.apparatus.adapter.innercore.game.entity; +import com.reider745.InnerCoreServer; +import com.reider745.api.ClientOnly; import com.reider745.entity.EntityMethod; import com.zhekasmirnov.apparatus.adapter.innercore.game.common.Vector3; import com.zhekasmirnov.innercore.api.NativeAPI; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.innercore.api.NativeItemInstance; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; public class StaticEntity { @@ -23,8 +24,8 @@ public static boolean exists(long entity) { return NativeAPI.isValidEntity(entity); } - public static ItemStack getCarriedItem(long entity) { - return new ItemStack(new NativeItemInstance(EntityMethod.getEntityCarriedItem(entity))); + public static ItemInstance getCarriedItem(long entity) { + return ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(entity)); } public static int getType(long entity) { @@ -37,17 +38,18 @@ public static Vector3 getVelocity(long entity) { return new Vector3(position); } - public static ItemStack getDroppedItem(long entity) { - return new ItemStack(new NativeItemInstance(EntityMethod.getItemFromDrop(entity))); + public static ItemInstance getDroppedItem(long entity) { + return ItemInstance.makeFrom(EntityMethod.getItemFromDrop(entity)); } public static int getExperienceOrbValue(long entity) { return EntityMethod.getExperienceOrbValue(entity); } - @Deprecated(since = "Zote") + @ClientOnly public static EntityActor newDroppedItem(float x, float y, float z, int id, int count, int data, - NativeItemInstanceExtra extra) { + ItemInstanceExtra extra) { + InnerCoreServer.useNotSupport("StaticEntity.newDroppedItem"); return new EntityActor(NativeAPI.spawnDroppedItem(x, y, z, id, count, data, extra)); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/item/ItemStack.java b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/item/ItemStack.java index a434645cb..5e49569b0 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/item/ItemStack.java +++ b/src/main/java/com/zhekasmirnov/apparatus/adapter/innercore/game/item/ItemStack.java @@ -1,149 +1,37 @@ package com.zhekasmirnov.apparatus.adapter.innercore.game.item; -import cn.nukkit.item.Item; +import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.mozilla.javascript.Scriptable; -import com.reider745.InnerCoreServer; -import com.zhekasmirnov.innercore.api.*; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Wrapper; - -public class ItemStack { - public int id, count, data; - public NativeItemInstanceExtra extra; +// this is a stub class for DungeonUtility, it MUST NOT be used in code +@Deprecated +public class ItemStack extends ItemInstance { + public ItemStack(int id, int count, int data, ItemInstanceExtra extra) { + super(id, count, data, extra); + } public ItemStack(int id, int count, int data, NativeItemInstanceExtra extra) { - this.id = id; - this.count = count; - this.data = data; - this.extra = NativeItemInstanceExtra.cloneExtra(extra); + super(id, count, data, extra); } public ItemStack(int id, int count, int data) { - this(id, count, data, null); - } - - public ItemStack() { - this(0, 0, 0, null); - } - - public ItemStack(ItemStack stack) { - this(stack.id, stack.count, stack.data, stack.extra); - } - - public ItemStack(NativeItemInstance itemInstance) { - this(itemInstance.id, itemInstance.count, itemInstance.data, itemInstance.extra); - } - - public ItemStack(ScriptableObject scriptable) { - this( - ScriptableObjectHelper.getIntProperty(scriptable, "id", 0), - ScriptableObjectHelper.getIntProperty(scriptable, "count", 0), - ScriptableObjectHelper.getIntProperty(scriptable, "data", 0), - NativeItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(scriptable, "extra", null))); - } - - public static ItemStack fromPtr(long ptr) { - InnerCoreServer.useNotCurrentSupport("ItemStack.fromPtr(ptr)"); - return new ItemStack(); - } - - public static ItemStack fromPtr(Item item) { - if (item != null) { - return new ItemStack(new NativeItemInstance(item)); - } else { - return new ItemStack(); - } + super(id, count, data); } - public static ItemStack parse(Object obj) { - while (obj instanceof Wrapper) { - obj = ((Wrapper) obj).unwrap(); - } - if (obj == null || "undefined".equals(obj.toString().toLowerCase())) { - return null; - } - if (obj instanceof ScriptableObject) { - return new ItemStack((ScriptableObject) obj); - } else if (obj instanceof ItemStack) { - return new ItemStack((ItemStack) obj); - } else if (obj instanceof JSONObject || obj instanceof CharSequence) { - JSONObject json; - try { - json = obj instanceof JSONObject ? (JSONObject) obj : new JSONObject(obj.toString()); - } catch (JSONException e) { - return null; - } - return new ItemStack(json.optInt("id", 0), json.optInt("count", 0), json.optInt("data", 0), - NativeItemInstanceExtra.fromJson(json.optJSONObject("extra"))); - } else if (obj instanceof NativeItemInstance) { - return new ItemStack((NativeItemInstance) obj); - } else if (obj instanceof Item) { - return ItemStack.fromPtr((Item) obj); - } - return null; + public ItemStack(ItemInstance rhs) { + super(rhs); } - public ScriptableObject asScriptable() { - ScriptableObject object = ScriptableObjectHelper.createEmpty(); - object.put("id", object, id); - object.put("count", object, count); - object.put("data", object, data); - object.put("extra", object, extra); - return object; + public ItemStack(ItemStack rhs) { + super(rhs); } - public JSONObject asJson() { - JSONObject json = new JSONObject(); - try { - json.put("id", id); - json.put("count", count); - json.put("data", data); - if (extra != null) { - JSONObject extraJson = extra.asJson(); - if (extraJson != null) { - json.put("extra", extraJson); - } - } - } catch (JSONException ignore) { - } - return json; - } - - public boolean isEmpty() { - return id == 0 && count == 0 && data == 0 && extra == null; - } - - public Item getExtraPtr() { - return NativeItemInstanceExtra.getValueOrNullPtr(extra); - } - - public int getMaxStackSize() { - return NativeItem.getMaxStackForId(id, data); - } - - public int getMaxDamage() { - return NativeItem.getMaxDamageForId(id, data); - } - - public String getItemName() { - return NativeItem.getNameForId(id, data, extra); - } - - public boolean isGlint() { - return NativeItem.isGlintItemInstance(id, data, extra); - } - - @Deprecated(since = "Zote") - public NativeItemModel getItemModel() { - return NativeItemModel.getForWithFallback(id, data); + public ItemStack() { } - @Override - public String toString() { - return "ItemStack{" + "id=" + id + ", count=" + count + ", data=" + data - + (extra != null ? ", extra=" + extra : "") + '}'; + public ItemStack(Scriptable scriptable) { + super(scriptable); } -} +} \ No newline at end of file diff --git a/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainer.java b/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainer.java index a44738603..e778b9469 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainer.java +++ b/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainer.java @@ -1,974 +1,7 @@ package com.zhekasmirnov.apparatus.api.container; -import com.reider745.item.ItemMethod; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; -import com.zhekasmirnov.apparatus.mcpe.NativePlayer; -import com.zhekasmirnov.apparatus.multiplayer.ThreadTypeMarker; -import com.zhekasmirnov.apparatus.multiplayer.server.ConnectedClient; -import com.zhekasmirnov.apparatus.multiplayer.util.entity.NetworkEntity; -import com.zhekasmirnov.apparatus.multiplayer.util.entity.NetworkEntityType; -import com.zhekasmirnov.apparatus.multiplayer.util.list.ConnectedClientList; -import com.zhekasmirnov.apparatus.util.Java8BackComp; -import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItem; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import com.zhekasmirnov.innercore.api.log.ICLog; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchField; -import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; -import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaver; -import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; +// this is a stub class for Refined Storage, it MUST NOT be used in code +@Deprecated +public class ItemContainer extends org.innercore.icstd.inventory.ItemContainer { -import java.util.*; - -public class ItemContainer implements WorkbenchField, ObjectSaverRegistry.IHashSaver { - private static final Object playerInventoryLock = new Object(); - - public static void loadClass() { - // forces class to load and register listeners - } - - private static final Map screenFactoryMap = new HashMap<>(); - private static final Map> clientEventListenerMap = new HashMap<>(); - private static final Map> clientOnOpenListenerMap = new HashMap<>(); - private static final Map> clientOnCloseListenerMap = new HashMap<>(); - - public static void registerScreenFactory(String name, UiScreenFactory factory) { - screenFactoryMap.put(name, factory); - } - - public static void addClientEventListener(String typeName, String packetName, ClientEventListener listener) { - Java8BackComp.computeIfAbsent(clientEventListenerMap, typeName, key -> new HashMap<>()).put(packetName, listener); - } - - public static void addClientOpenListener(String typeName, ClientOnOpenListener listener) { - Java8BackComp.computeIfAbsent(clientOnOpenListenerMap, typeName, key -> new ArrayList<>()).add(listener); - } - - public static void addClientCloseListener(String typeName, ClientOnCloseListener listener) { - Java8BackComp.computeIfAbsent(clientOnCloseListenerMap, typeName, key -> new ArrayList<>()).add(listener); - } - - public static ItemContainer getClientContainerInstance(String name) { - NetworkEntity entity = NetworkEntity.getClientEntityInstance(name); - if (entity != null) { - Object target = entity.getTarget(); - if (target instanceof ItemContainer) { - return (ItemContainer) target; - } - } - return null; - } - - - @SuppressWarnings("deprecation") - private static final int saverId = ObjectSaverRegistry.registerSaver("_container2", new ObjectSaver() { - @Override - public Object read(ScriptableObject input) { - com.zhekasmirnov.innercore.api.mod.ui.container.Container con = new com.zhekasmirnov.innercore.api.mod.ui.container.Container(); - con.slots = input; - return new ItemContainer(con); - } - - @Override - public ScriptableObject save(Object input) { - if (input instanceof ItemContainer) { - return ((ItemContainer) input).asLegacyContainer(false).slots; - } - return null; - } - }); - - private static final NetworkEntityType containerNetworkEntityType = new NetworkEntityType("sys.container") - .setClientListSetupListener((list, target, entity) -> { - ItemContainer container = (ItemContainer) target; - list.addListener(new ConnectedClientList.Listener() { - @Override - public void onAdd(ConnectedClient client) { - // this event is sent too early, ui is not yet initialized, so on open listener is not called - } - - @Override - public void onRemove(ConnectedClient client) { - for (ServerOnCloseListener listener : container.onCloseListeners) { - listener.onClose(container, client); - } - } - }); - }) - .addServerPacketListener("close", (target, entity, client, packetData, extra) -> { - ItemContainer container = (ItemContainer) target; - container.closeFor(client); - }) - .addServerPacketListener("inv_to_slot", (target, entity, client, packetData, extra) -> { - ItemContainer container = (ItemContainer) target; - JSONObject packet = (JSONObject) packetData; - synchronized (container.transactionLock) { - long player = client.getPlayerUid(); - container.handleInventoryToSlotTransaction(player, packet.optInt("inv"), packet.optString("name"), packet.optInt("amount")); - container.sendChanges(); - } - }) - .addServerPacketListener("slot_to_inv", (target, entity, client, packetData, extra) -> { - ItemContainer container = (ItemContainer) target; - JSONObject packet = (JSONObject) packetData; - synchronized (container.transactionLock) { - long player = client.getPlayerUid(); - container.handleSlotToInventoryTransaction(player, packet.optString("slot"), packet.optInt("amount")); - container.sendChanges(); - } - }) - .addServerPacketListener("slot_to_slot", (target, entity, client, packetData, extra) -> { - ItemContainer container = (ItemContainer) target; - JSONObject packet = (JSONObject) packetData; - synchronized (container.transactionLock) { - long player = client.getPlayerUid(); - container.handleSlotToSlotTransaction(player, packet.optString("slot1"), packet.optString("slot2"), packet.optInt("amount")); - container.sendChanges(); - } - }) - .addServerPacketListener("bindings", (target, entity, client, packetData, extra) -> { - ItemContainer container = (ItemContainer) target; - JSONObject packet = (JSONObject) packetData; - container.handleDirtyBindingsPacket(client, packet); - container.sendChanges(); - }) - .addServerPacketListener("event", (target, entity, client, packetData, extra) -> { - ItemContainer container = (ItemContainer) target; - ServerEventListener listener = container.serverEventListenerMap.get(extra); - if (listener != null) { - try{ - listener.receive(container, client, packetData); - }catch (Exception e){ - ICLog.e("NETWORK", "ItemContainer: "+extra, e); - } - - } - }) - .setClientAddPacketFactory((target, entity, client) -> { - ItemContainer container = (ItemContainer) target; - JSONObject packet = new JSONObject(); - - try { - JSONObject slots = new JSONObject(); - packet.put("clientType", container.clientContainerTypeName); - packet.put("slots", slots); - for (Map.Entry entry : container.slotMap.entrySet()) { - slots.put(entry.getKey(), entry.getValue().asJson()); - } - packet.put("bindings", new JSONObject(container.bindingsMap)); - } catch (JSONException ignore) { } - - return packet; - }); - private int id; - - @Override - public int getId() { - return id; - } - - @Override - public void setId(int id) { - this.id = id; - } - - - public interface Transaction { - void run(ItemContainer container); - } - - @Deprecated(since = "Zote") - public interface UiScreenFactory { - com.zhekasmirnov.innercore.api.mod.ui.window.IWindow getByName(ItemContainer container, String name); - } - - public interface DirtySlotListener { - void onMarkedDirty(ItemContainer container, String name, ItemContainerSlot slot); - } - - public interface TransferPolicy { - int transfer(ItemContainer container, String name, int id, int count, int data, NativeItemInstanceExtra extra, long player); - } - - public interface BindingValidator { - Object validate(ItemContainer container, String bindingComposedName, Object value, long player); - } - - @Deprecated(since = "Zote") - public interface ClientEventListener { - void receive(ItemContainer container, com.zhekasmirnov.innercore.api.mod.ui.window.IWindow window, ScriptableObject windowContent, Object packetData); - } - - public interface ServerEventListener { - void receive(ItemContainer container, ConnectedClient client, Object packetData); - } - - @Deprecated(since = "Zote") - public interface ClientOnOpenListener { - void onOpen(ItemContainer container, String screenName); - } - - @Deprecated(since = "Zote") - public interface ClientOnCloseListener { - void onClose(ItemContainer container); - } - - public interface ServerOnOpenListener { - void onOpen(ItemContainer container, ConnectedClient client, String screenName); - } - - public interface ServerOnCloseListener { - void onClose(ItemContainer container, ConnectedClient client); - } - - - public final boolean isServer; - private final NetworkEntity networkEntity; - private Object parent = null; - - public final Object transactionLock = new Object(); - private final Map slotMap = new HashMap<>(); - private final Set dirtySlotSet = new HashSet<>(); - - private final Map bindingsMap = new HashMap<>(); - private final Map dirtyBindingsMap = new HashMap<>(); - - private TransferPolicy globalAddTransferPolicy = null; - private TransferPolicy globalGetTransferPolicy = null; - private final Map addTransferPolicyMap = new HashMap<>(); - private final Map getTransferPolicyMap = new HashMap<>(); - private DirtySlotListener globalDirtySlotListener = null; - private final Map dirtySlotListenerMap = new HashMap<>(); - - private BindingValidator globalBindingValidator = null; - private final Map bindingValidatorMap = new HashMap<>(); - - private String clientContainerTypeName = null; - private final Map serverEventListenerMap = new HashMap<>(); - private final List onOpenListeners = new ArrayList<>(); - private final List onCloseListeners = new ArrayList<>(); - - // container slots interface for legacy container interface - public final Scriptable slots = ScriptableObjectHelper.createEmpty(); - - - private ItemContainer(String entityName) { - ThreadTypeMarker.assertServerThread(); - ObjectSaverRegistry.registerObject(this, saverId); - this.isServer = true; - if (entityName != null) { - networkEntity = new NetworkEntity(containerNetworkEntityType, this, entityName); - } else { - networkEntity = new NetworkEntity(containerNetworkEntityType, this); - } - } - - public ItemContainer() { - this((String) null); - } - - // copy legacy container - @SuppressWarnings("deprecation") - public ItemContainer(com.zhekasmirnov.innercore.api.mod.ui.container.Container container) { - this(); - for (Object id : container.slots.getAllIds()) { - Object slotScriptable = container.slots.get(id.toString(), container.slots); - if (slotScriptable instanceof ScriptableObject) { - setSlot(id.toString(), new ItemContainerSlot((ScriptableObject) slotScriptable)); - } - } - } - - public NetworkEntity getNetworkEntity() { - return networkEntity; - } - - public String getNetworkName() { - return networkEntity != null ? networkEntity.getName() : null; - } - - @Deprecated(since = "Zote") - public ItemContainerUiHandler getUiAdapter() { - if (isServer) { - throw new IllegalStateException(); - } - return null; - } - - @Deprecated(since = "Zote") - public com.zhekasmirnov.innercore.api.mod.ui.window.IWindow getWindow() { - return getUiAdapter().getWindow(); - } - - @Deprecated(since = "Zote") - public ScriptableObject getWindowContent() { - com.zhekasmirnov.innercore.api.mod.ui.window.IWindow window = getUiAdapter().getWindow(); - return window != null ? window.getContent() : null; - } - - public void removeEntity() { - if (!isServer) { - throw new IllegalStateException(); - } - ThreadTypeMarker.assertServerThread(); - networkEntity.remove(); - } - - public void setParent(Object parent) { - this.parent = parent; - } - - public Object getParent() { - return parent; - } - - - public ItemContainer setGlobalAddTransferPolicy(TransferPolicy globalAddTransferPolicy) { - this.globalAddTransferPolicy = globalAddTransferPolicy; - return this; - } - - public ItemContainer setGlobalGetTransferPolicy(TransferPolicy globalGetTransferPolicy) { - this.globalGetTransferPolicy = globalGetTransferPolicy; - return this; - } - - public ItemContainer setSlotAddTransferPolicy(String name, TransferPolicy policy) { - addTransferPolicyMap.put(name, policy); - return this; - } - - public ItemContainer setSlotGetTransferPolicy(String name, TransferPolicy policy) { - getTransferPolicyMap.put(name, policy); - return this; - } - - public ItemContainer setGlobalDirtySlotListener(DirtySlotListener globalDirtySlotListener) { - this.globalDirtySlotListener = globalDirtySlotListener; - return this; - } - - public ItemContainer setDirtySlotListener(String name, DirtySlotListener listener) { - dirtySlotListenerMap.put(name, listener); - return this; - } - - public void sealSlot(String slotName) { - setSlotAddTransferPolicy(slotName, (container, name, id, count, data, extra, player) -> 0); - setSlotAddTransferPolicy(slotName, (container, name, id, count, data, extra, player) -> 0); - } - - public void sealAllSlots() { - setGlobalAddTransferPolicy((container, name, id, count, data, extra, player) -> 0); - setGlobalGetTransferPolicy((container, name, id, count, data, extra, player) -> 0); - } - - public TransferPolicy getAddTransferPolicy(String slot) { - return Java8BackComp.getOrDefault(addTransferPolicyMap, slot, globalAddTransferPolicy); - } - - public TransferPolicy getGetTransferPolicy(String slot) { - return Java8BackComp.getOrDefault(getTransferPolicyMap, slot, globalGetTransferPolicy); - } - - - public void setGlobalBindingValidator(BindingValidator globalBindingValidator) { - this.globalBindingValidator = globalBindingValidator; - } - - public void setBindingValidator(String composedBindingName, BindingValidator validator) { - this.bindingValidatorMap.put(composedBindingName, validator); - } - - public BindingValidator getBindingValidator(String composedBindingName) { - return Java8BackComp.getOrDefault(bindingValidatorMap, composedBindingName, globalBindingValidator); - } - - - - public void runTransaction(Transaction transaction) { - synchronized (transactionLock) { - transaction.run(this); - } - } - - public ItemContainerSlot getSlot(String name) { - if (!slotMap.containsKey(name)) { - ItemContainerSlot slot = new ItemContainerSlot(); - slot.setContainer(this, name); - slotMap.put(name, slot); - slots.put(name, slots, slot); - return slot; - } else { - return slotMap.get(name); - } - } - - @Deprecated - public ItemContainerSlot getFullSlot(String name) { - return getSlot(name); - } - - - public void markSlotDirty(String name) { - synchronized (transactionLock) { - if (isServer) { - ItemContainerSlot slot = slotMap.get(name); - if (slot != null) { - dirtySlotSet.add(name); - if (globalDirtySlotListener != null) { - globalDirtySlotListener.onMarkedDirty(this, name, slot); - } - DirtySlotListener listener = dirtySlotListenerMap.get(name); - if (listener != null) { - listener.onMarkedDirty(this, name, slot); - } - } - } - } - } - - public void markAllSlotsDirty() { - synchronized (transactionLock) { - if (isServer) { - for (String name : slotMap.keySet()) { - markSlotDirty(name); - } - } - } - } - - public void setSlot(String name, ItemContainerSlot slot) { - slot.setContainer(this, name); - slotMap.put(name, slot); - slots.put(name, slots, slot); - markSlotDirty(name); - } - - public void setSlot(String name, int id, int count, int data, NativeItemInstanceExtra extra) { - ItemContainerSlot slot = slotMap.get(name); - if (slot != null) { - slot.setSlot(id, count, data, extra); - } else { - setSlot(name, new ItemContainerSlot(id, count, data, extra)); - } - } - - public void setSlot(String name, int id, int count, int data) { - setSlot(name, id, count, data, null); - } - - public int addToSlot(String name, int id, int count, int data, NativeItemInstanceExtra extra, long player) { - if (count <= 0) { - return 0; - } - ItemContainerSlot slot = getSlot(name); - if (slot != null && (slot.id == 0 || slot.id == id && slot.data == data && slot.extra == null)) { - int amount; - TransferPolicy policy = getAddTransferPolicy(name); - if (policy != null) { - amount = policy.transfer(this, name, id, count, data, extra, player); - } else { - amount = Math.min(slot.count + count, NativeItem.getMaxStackForId(id, data)) - slot.count; - } - if (amount > 0) { - slot.count += amount; - slot.id = id; - slot.data = data; - slot.extra = extra; - markSlotDirty(name); - return amount; - } - } - return 0; - } - - public int getFromSlot(String name, int id, int count, int data, NativeItemInstanceExtra extra, long player) { - if (count <= 0) { - return 0; - } - ItemContainerSlot slot = getSlot(name); - if (slot != null && slot.id == id && slot.data == data && (slot.extra == null || slot.extra.equals(extra))) { - TransferPolicy policy = getGetTransferPolicy(name); - int amount = Math.min(slot.count, count); - if (policy != null) { - amount = policy.transfer(this, name, id, amount, data, extra, player); - } - if (amount > 0) { - slot.count -= amount; - slot.validate(); - markSlotDirty(name); - return amount; - } - } - return 0; - } - - public void sendChanges() { - if (isServer) { - if (!dirtySlotSet.isEmpty()) { - JSONObject slotPacket = new JSONObject(); - synchronized (transactionLock) { - try { - for (String name : dirtySlotSet) { - ItemContainerSlot slot = slotMap.get(name); - if (slot != null) { - slotPacket.put(name, slot.asJson()); - } - } - dirtySlotSet.clear(); - } catch (JSONException ignore) { - } - } - networkEntity.send("slots", slotPacket); - } - if (!dirtyBindingsMap.isEmpty()) { - synchronized (bindingsMap) { - try{ - networkEntity.send("bindings", new JSONObject(dirtyBindingsMap)); - dirtyBindingsMap.clear(); - }catch (Exception e){ - //Logger.error(dirtyBindingsMap.toString(), e); - } - } - } - } - } - - public void dropAt(NativeBlockSource blockSource, float x, float y, float z) { - synchronized (transactionLock) { - for (ItemContainerSlot slot : slotMap.values()) { - slot.dropAt(blockSource, x, y, z); - } - } - sendChanges(); - } - - public void validateAll() { - synchronized (transactionLock) { - for (ItemContainerSlot slot : slotMap.values()) { - slot.validate(); - } - } - sendChanges(); - } - - public void validateSlot(String name) { - getSlot(name).validate(); - } - - public void clearSlot(String name) { - getSlot(name).clear(); - } - - public void dropSlot(NativeBlockSource blockSource, String name, float x, float y, float z) { - getSlot(name).dropAt(blockSource, x, y, z); - } - - - // default transactions - - @Deprecated(since = "Zote") - public void sendInventoryToSlotTransaction(int inventorySlot, String slotName, int amount) { - if (isServer) { - throw new IllegalStateException(); - } - try { - JSONObject packet = new JSONObject(); - packet.put("inv", inventorySlot); - packet.put("name", slotName); - packet.put("amount", amount); - networkEntity.send("inv_to_slot", packet); - } catch (JSONException ignore) { } - } - - void handleInventoryToSlotTransaction(long playerUid, int inventorySlot, String slotName, int amount) { - if (!isServer) { - throw new IllegalStateException(); - } - - if (slotName != null && amount > 0 && inventorySlot >= 0 && inventorySlot < 36) { - NativePlayer player = new NativePlayer(playerUid); - if (player.isValid()) { - synchronized (playerInventoryLock) { - ItemStack itemInstance = player.getInventorySlot(inventorySlot); - if (itemInstance.id != 0 && itemInstance.count > 0) { - int id = itemInstance.id; - int count = itemInstance.count; - int data = itemInstance.data; - NativeItemInstanceExtra extra = itemInstance.extra; - if (amount > itemInstance.count) { - amount = itemInstance.count; - } - amount = addToSlot(slotName, id, amount, data, extra, playerUid); - count -= amount; - if (count > 0) { - player.setInventorySlot(inventorySlot, id, count, data, extra); - } else { - player.setInventorySlot(inventorySlot, 0, 0, 0, null); - } - } - } - } - } - } - - @Deprecated(since = "Zote") - public void sendSlotToSlotTransaction(String slot1, String slot2, int amount) { - if (isServer) { - throw new IllegalStateException(); - } - try { - JSONObject packet = new JSONObject(); - packet.put("slot1", slot1); - packet.put("slot2", slot2); - packet.put("amount", amount); - networkEntity.send("slot_to_slot", packet); - } catch (JSONException ignore) { } - } - - void handleSlotToSlotTransaction(long player, String slotName1, String slotName2, int amount) { - if (!isServer) { - throw new IllegalStateException(); - } - } - - @Deprecated(since = "Zote") - public void sendSlotToInventoryTransaction(String slot, int amount) { - if (isServer) { - throw new IllegalStateException(); - } - try { - JSONObject packet = new JSONObject(); - packet.put("slot", slot); - packet.put("amount", amount); - networkEntity.send("slot_to_inv", packet); - } catch (JSONException ignore) { } - } - - void handleSlotToInventoryTransaction(long playerUid, String slotName, int amount) { - if (!isServer) { - throw new IllegalStateException(); - } - - /*if (amount > 0) { - ItemContainerSlot slot = getSlot(slotName); - if (slot.id != 0 && slot.count > 0) { - NativePlayer player = new NativePlayer(playerUid); - if (player.isValid()) { - int id = slot.id; - int data = slot.data; - NativeItemInstanceExtra extra = slot.extra; - amount = getFromSlot(slotName, id, amount, data, extra, playerUid); - if (amount > 0) { - synchronized (playerInventoryLock) { - player.addItemToInventory(id, amount, data, extra, true); - } - } - } - } - }*/ - //Фикс максимально стака - if(amount > 0){ - ItemContainerSlot slot = getSlot(slotName); - if (slot.id != 0 && slot.count > 0) { - NativePlayer player = new NativePlayer(playerUid); - if (player.isValid()) { - int id = slot.id; - int data = slot.data; - NativeItemInstanceExtra extra = slot.extra; - amount = getFromSlot(slotName, id, amount, data, extra, playerUid); - int maxStack = ItemMethod.getMaxStackForId(id, data); - - if (amount > 0) { - if(amount <= maxStack){ - synchronized (playerInventoryLock) { - player.addItemToInventory(id, amount, data, extra, true); - } - }else{ - synchronized (playerInventoryLock) { - while (amount > 0) { - player.addItemToInventory(id, Math.min(amount, maxStack), data, extra, true); - amount -= maxStack; - } - } - } - } - } - } - } - } - - - void sendDirtyClientBinding(String key, Object value) { - if (!isServer) { - throw new IllegalStateException(); - } - try { - JSONObject packet = new JSONObject(); - packet.put(key, value); - networkEntity.send("bindings", packet); - } catch (JSONException ignore) { } - } - - void handleDirtyBindingsPacket(ConnectedClient client, JSONObject packet) { - long playerUid = client.getPlayerUid(); - for (Iterator it = packet.keys(); it.hasNext(); ) { - String key = it.next(); - Object value = packet.opt(key); - if (value instanceof Boolean || value instanceof Number || value instanceof String) { - BindingValidator validator = getBindingValidator(key); - if (validator != null) { - value = validator.validate(this, key, value, playerUid); - } - if (value != null) { - synchronized (bindingsMap) { - bindingsMap.put(key, value); - dirtyBindingsMap.put(key, value); - } - } - } - } - } - - - public void setBinding(String composedBindingName, Object value) { - if (value instanceof CharSequence) { - value = value.toString(); - } else if (!(value instanceof Number || value instanceof Boolean)) { - throw new IllegalArgumentException("binding value must be number, string or boolean"); - } - synchronized (bindingsMap) { - bindingsMap.put(composedBindingName, value); - dirtyBindingsMap.put(composedBindingName, value); - } - } - - @Deprecated(since = "Zote") - public void setClientBinding(String composedBindingName, Object value) { - getUiAdapter().setBindingByComposedName(composedBindingName, value); - synchronized (bindingsMap) { - bindingsMap.put(composedBindingName, value); - } - } - - public Object getBinding(String composedBindingName) { - return bindingsMap.get(composedBindingName); - } - - public void setBinding(String elementName, String bindingName, Object value) { - setBinding(elementName + "::" + bindingName, value); - } - - @Deprecated(since = "Zote") - public void setClientBinding(String elementName, String bindingName, Object value) { - setClientBinding(elementName + "::" + bindingName, value); - } - - public Object getBinding(String elementName, String bindingName) { - return getBinding(elementName + "::" + bindingName); - } - - public void setScale(String elementName, float value) { - setBinding(elementName, "value", value); - } - - @Deprecated(since = "Zote") - public void setClientScale(String elementName, float value) { - setClientBinding(elementName, "value", value); - } - - public Object getValue(String elementName, float value) { - return getBinding(elementName, "value"); - } - - public void setText(String elementName, String text) { - setBinding(elementName, "text", text); - } - - @Deprecated(since = "Zote") - public void setClientText(String elementName, String text) { - setClientBinding(elementName, "text", text); - } - - public String getText(String elementName) { - Object binding = getBinding(elementName, "text"); - return binding instanceof CharSequence ? binding.toString() : null; - } - - - @Deprecated(since = "Zote") - public void setClientContainerTypeName(String clientContainerTypeName) { - this.clientContainerTypeName = clientContainerTypeName; - } - - @Deprecated(since = "Zote") - public String getClientContainerTypeName() { - return clientContainerTypeName; - } - - public void addServerEventListener(String name, ServerEventListener listener) { - serverEventListenerMap.put(name, listener); - } - - public void addServerOpenListener(ServerOnOpenListener listener) { - onOpenListeners.add(listener); - } - - public void addServerCloseListener(ServerOnCloseListener listener) { - onCloseListeners.add(listener); - } - - public void sendEvent(String name, Object data) { - if (isServer) { - networkEntity.send("event#" + name, data); - } - } - - public void sendEvent(ConnectedClient client, String name, Object data) { - if (isServer) { - networkEntity.send(client, "event#" + name, data); - } - } - - public void sendResponseEvent(String name, Object data) { - if (isServer) { - networkEntity.respond("event#" + name, data); - } - } - - - public void openFor(ConnectedClient client, String screenName) { - if (!isServer) { - throw new IllegalStateException(); - } - networkEntity.getClients().add(client); - networkEntity.send(client, "open", screenName); - for (ServerOnOpenListener listener : onOpenListeners) { - listener.onOpen(this, client, screenName); - } - } - - public void closeFor(ConnectedClient client) { - if (!isServer) { - throw new IllegalStateException(); - } - networkEntity.getClients().remove(client); - } - - public void close() { - if (!isServer) { - throw new IllegalStateException(); - } - networkEntity.getClients().clear(); - } - - @Deprecated(since = "Zote") - public void sendClosed() { - if (isServer) { - throw new IllegalStateException(); - } - List onCloseListener = clientOnCloseListenerMap.get(clientContainerTypeName); - if (onCloseListener != null) { - for (ClientOnCloseListener listener : onCloseListener) { - listener.onClose(this); - } - } - networkEntity.send("close", ""); - } - - - // saves - - private boolean globalSlotSavingEnabled = true; - - public void setGlobalSlotSavingEnabled(boolean globalSlotSavingEnabled) { - this.globalSlotSavingEnabled = globalSlotSavingEnabled; - } - - public boolean isGlobalSlotSavingEnabled() { - return globalSlotSavingEnabled; - } - - public void setSlotSavingEnabled(String name, boolean enabled) { - getSlot(name).setSavingEnabled(enabled); - } - - public void resetSlotSavingEnabled(String name) { - getSlot(name).resetSavingEnabled(); - } - - public boolean isSlotSavingEnabled(String name) { - return slotMap.containsKey(name) ? getSlot(name).isSavingEnabled() : globalSlotSavingEnabled; - } - - - // legacy container backcomp - - public boolean isLegacyContainer() { - return false; - } - - @Deprecated(since = "Zote") - public com.zhekasmirnov.innercore.api.mod.ui.container.Container asLegacyContainer(boolean allSlots) { - com.zhekasmirnov.innercore.api.mod.ui.container.Container container = new com.zhekasmirnov.innercore.api.mod.ui.container.Container(this); - for (Map.Entry entry : slotMap.entrySet()) { - ItemContainerSlot slot = entry.getValue(); - if (!slot.isEmpty() && (allSlots || slot.isSavingEnabled())) { - container.slots.put(entry.getKey(), container.slots, slot.asScriptable()); - } - } - return container; - } - - @Deprecated(since = "Zote") - public com.zhekasmirnov.innercore.api.mod.ui.container.Container asLegacyContainer() { - return asLegacyContainer(true); - } - - - private String legacyWorkbenchFieldPrefix = null; - private int workbenchFieldSize = 3; - - public void setWorkbenchFieldPrefix(String legacyWorkbenchFieldPrefix) { - this.legacyWorkbenchFieldPrefix = legacyWorkbenchFieldPrefix; - } - - public void setWorkbenchFieldSize(int workbenchFieldSize) { - this.workbenchFieldSize = workbenchFieldSize; - } - - @Override - public AbstractSlot getFieldSlot(int index) { - return getSlot(legacyWorkbenchFieldPrefix + index); - } - - @Override - public AbstractSlot getFieldSlot(int x, int y) { - if (x >= 0 && y >= 0 && x < workbenchFieldSize && y < workbenchFieldSize) { - return getSlot(legacyWorkbenchFieldPrefix + (y * workbenchFieldSize + x)); - } else { - return null; - } - } - - @Override - public Scriptable asScriptableField() { - Object[] field = new Object[workbenchFieldSize * workbenchFieldSize]; - for (int i = 0; i < field.length; i++) { - field[i] = getFieldSlot(i); - } - return ScriptableObjectHelper.createArray(field); - } - - @Override - public int getWorkbenchFieldSize() { - return workbenchFieldSize; - } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainerUiHandler.java b/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainerUiHandler.java index f54fa5317..11c7e29ca 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainerUiHandler.java +++ b/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainerUiHandler.java @@ -1,7 +1,9 @@ package com.zhekasmirnov.apparatus.api.container; import com.zhekasmirnov.apparatus.multiplayer.Network; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemContainer; +import org.innercore.icstd.inventory.ItemContainerSlot; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.ui.container.UiAbstractContainer; import com.zhekasmirnov.innercore.api.mod.ui.container.UiVisualSlotImpl; import com.zhekasmirnov.innercore.api.mod.ui.elements.UIElement; @@ -14,7 +16,6 @@ import java.util.Iterator; import java.util.Map; -@Deprecated(since = "Zote") public class ItemContainerUiHandler implements UiAbstractContainer { private final ItemContainer container; @@ -108,7 +109,7 @@ public int getData() { } @Override - public NativeItemInstanceExtra getExtra() { + public ItemInstanceExtra getExtra() { if (slot == null) { slot = container.getSlot(name); } @@ -179,14 +180,14 @@ void applyAllBindingsFromMap() { } } - void setBindingByComposedName(String name, Object value) { + public void setBindingByComposedName(String name, Object value) { String[] parts = name.split("::"); if (parts.length == 2) { setBinding(parts[0], parts[1], value); } } - void receiveBindingsFromServer(JSONObject bindings) { + public void receiveBindingsFromServer(JSONObject bindings) { if (bindings == null) { return; } diff --git a/src/main/java/com/zhekasmirnov/apparatus/api/player/armor/ActorArmorHandler.java b/src/main/java/com/zhekasmirnov/apparatus/api/player/armor/ActorArmorHandler.java index 43d76bbb3..d34142381 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/api/player/armor/ActorArmorHandler.java +++ b/src/main/java/com/zhekasmirnov/apparatus/api/player/armor/ActorArmorHandler.java @@ -1,29 +1,28 @@ package com.zhekasmirnov.apparatus.api.player.armor; import com.zhekasmirnov.apparatus.adapter.innercore.game.entity.EntityActor; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; import com.zhekasmirnov.apparatus.job.JobExecutor; import com.zhekasmirnov.apparatus.multiplayer.Network; +import org.innercore.icstd.inventory.ItemInstance; import java.util.HashMap; import java.util.Map; public class ActorArmorHandler { public interface OnTickListener { - Object onTick(ItemStack item, int slot, long player); + Object onTick(ItemInstance item, int slot, long player); } public interface OnHurtListener { - Object onHurt(ItemStack item, int slot, long player, int damageValue, int damageType, long attacker, - boolean bool1, boolean bool2); + Object onHurt(ItemInstance item, int slot, long player, int damageValue, int damageType, long attacker, boolean bool1, boolean bool2); } public interface OnTakeOffListener { - void onTakeOff(ItemStack item, int slot, long player); + void onTakeOff(ItemInstance item, int slot, long player); } public interface OnTakeOnListener { - void onTakeOn(ItemStack item, int slot, long player); + void onTakeOn(ItemInstance item, int slot, long player); } private static final Map onTickListenerMap = new HashMap<>(); @@ -62,9 +61,10 @@ public static void registerLocalOnTakeOffListener(int id, OnTakeOffListener list onLocalTakeOffListenerMap.put(id, listener); } + private final boolean isLocal; private final EntityActor actor; - private final ItemStack[] armorItems = new ItemStack[4]; + private final ItemInstance[] armorItems = new ItemInstance[4]; private final JobExecutor instantExecutor = Network.getSingleton().getInstantJobExecutor(); private final JobExecutor delayedExecutor = Network.getSingleton().getServerThreadJobExecutor(); @@ -73,25 +73,23 @@ public ActorArmorHandler(EntityActor actor, boolean isLocal) { this.isLocal = isLocal; this.actor = actor; for (int slot = 0; slot < 4; slot++) { - armorItems[slot] = new ItemStack(); + armorItems[slot] = new ItemInstance(); } } public void onTick() { long actorUid = actor.getUid(); for (int slot = 0; slot < 4; slot++) { - ItemStack currentArmor = actor.getArmorSlot(slot); - ItemStack lastArmor = armorItems[slot]; + ItemInstance currentArmor = actor.getArmorSlot(slot); + ItemInstance lastArmor = armorItems[slot]; int finalSlot = slot; if (currentArmor.id != lastArmor.id) { instantExecutor.add(() -> { - OnTakeOffListener takeOffListener = (isLocal ? onLocalTakeOffListenerMap : onTakeOffListenerMap) - .get(lastArmor.id); + OnTakeOffListener takeOffListener = (isLocal ? onLocalTakeOffListenerMap : onTakeOffListenerMap).get(lastArmor.id); if (takeOffListener != null) { takeOffListener.onTakeOff(lastArmor, finalSlot, actorUid); } - OnTakeOnListener takeOnListener = (isLocal ? onLocalTakeOnListenerMap : onTakeOnListenerMap) - .get(currentArmor.id); + OnTakeOnListener takeOnListener = (isLocal ? onLocalTakeOnListenerMap : onTakeOnListenerMap).get(currentArmor.id); if (takeOnListener != null) { takeOnListener.onTakeOn(currentArmor, finalSlot, actorUid); } @@ -103,9 +101,8 @@ public void onTick() { instantExecutor.add(() -> { Object resultItem = tickListener.onTick(currentArmor, finalSlot, actorUid); if (resultItem != null) { - ItemStack resultItemStack = ItemStack.parse(resultItem); - // do not save this item in armorItems, so on next tick callbacks will be - // triggered in case of id change + ItemInstance resultItemStack = ItemInstance.parse(resultItem, true); + // do not save this item in armorItems, so on next tick callbacks will be triggered in case of id change if (resultItemStack != null) { actor.setArmorSlot(finalSlot, resultItemStack); } @@ -118,22 +115,20 @@ public void onTick() { } public void onHurt(long attacker, int damageValue, int damageType, boolean bool1, boolean bool2) { - if (isLocal) - return; + if (isLocal) return; long actorUid = actor.getUid(); for (int slot = 0; slot < 4; slot++) { - ItemStack armor = actor.getArmorSlot(slot); + ItemInstance armor = actor.getArmorSlot(slot); OnHurtListener listener = onHurtListenerMap.get(armor.id); if (listener != null) { int finalSlot = slot; instantExecutor.add(() -> { - Object resultItem = listener.onHurt(armor, finalSlot, actorUid, damageValue, damageType, attacker, - bool1, bool2); + Object resultItem = listener.onHurt(armor, finalSlot, actorUid, damageValue, damageType, attacker, bool1, bool2); if (resultItem != null) { delayedExecutor.add(() -> { if (actor.getHealth() > 0) { - ItemStack resultItemStack = ItemStack.parse(resultItem); + ItemInstance resultItemStack = ItemInstance.parse(resultItem, true); if (resultItemStack != null) { actor.setArmorSlot(finalSlot, resultItemStack); } diff --git a/src/main/java/com/zhekasmirnov/apparatus/ecs/types/item/ArmorItemComponent.java b/src/main/java/com/zhekasmirnov/apparatus/ecs/types/item/ArmorItemComponent.java index 69de613e3..aab8e8b35 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/ecs/types/item/ArmorItemComponent.java +++ b/src/main/java/com/zhekasmirnov/apparatus/ecs/types/item/ArmorItemComponent.java @@ -1,9 +1,9 @@ package com.zhekasmirnov.apparatus.ecs.types.item; import com.zhekasmirnov.apparatus.ecs.core.TypeIndexMap; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; import com.zhekasmirnov.innercore.api.commontypes.ScriptableParams; import com.zhekasmirnov.innercore.api.runtime.other.ArmorRegistry; +import org.innercore.icstd.inventory.ItemInstance; public class ArmorItemComponent implements ArmorRegistry.IArmorCallback { public static final int COMPONENT_ID = TypeIndexMap.getTypeIndex("ArmorItemComponent"); diff --git a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeBlockSource.java b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeBlockSource.java index 97df54a1f..1cc9cf58a 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeBlockSource.java +++ b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeBlockSource.java @@ -1,551 +1,52 @@ package com.zhekasmirnov.apparatus.mcpe; -import cn.nukkit.Server; -import cn.nukkit.blockentity.BlockEntity; -import cn.nukkit.entity.Entity; -import cn.nukkit.level.Level; -import cn.nukkit.math.Vector3; -import com.zhekasmirnov.mcpe161.InnerCore; -import it.unimi.dsi.fastutil.longs.LongArrayList; - -import com.reider745.InnerCoreServer; -import com.reider745.api.CallbackHelper; -import com.reider745.api.ZoteOnly; -import com.reider745.entity.EntityMethod; -import com.reider745.world.BlockSourceMethods; -import com.zhekasmirnov.apparatus.adapter.innercore.game.block.BlockBreakResult; import com.zhekasmirnov.apparatus.adapter.innercore.game.block.BlockState; -import com.zhekasmirnov.apparatus.adapter.innercore.game.entity.StaticEntity; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.apparatus.util.Java8BackComp; -import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeAPI; -import com.zhekasmirnov.innercore.api.NativeCallback; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import com.zhekasmirnov.innercore.api.NativeTileEntity; -import com.zhekasmirnov.innercore.api.commontypes.Coords; -import com.zhekasmirnov.innercore.api.log.ICLog; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import com.zhekasmirnov.innercore.api.runtime.Callback; -import org.mozilla.javascript.NativeArray; -import org.mozilla.javascript.ScriptableObject; - -import java.util.HashMap; -import java.util.Map; - -public class NativeBlockSource { - private static final Map defaultBlockSourceForDimensions = new HashMap<>(); - - public static NativeBlockSource getDefaultForDimension(int dimension) { - synchronized (defaultBlockSourceForDimensions) { - try { - return Java8BackComp.computeIfAbsent(defaultBlockSourceForDimensions, dimension, - key -> new NativeBlockSource(dimension)); - } catch (IllegalArgumentException e) { - Logger.error(e.getMessage()); - return null; - } - } - } - - @ZoteOnly - public static NativeBlockSource getDefault() { - Level level = BlockSourceMethods.getDefaultLevel(); - return level != null ? getFromCallbackPointer(level) : null; - } - - public static NativeBlockSource getDefaultForActor(long actor) { - Entity entity = EntityMethod.getEntityById(actor); - return entity != null ? getFromCallbackPointer(entity.getLevel()) : null; - } - - @ZoteOnly - public static NativeBlockSource getDefaultForLevel(String name) { - Level level = BlockSourceMethods.getLevelForName(name); - return level != null ? getFromCallbackPointer(level) : null; - } - - public static NativeBlockSource getCurrentWorldGenRegion() { - Level level = CallbackHelper.getForCurrentThread(); - if(level == null) level = Server.getInstance().getDefaultLevel(); - return level != null ? new NativeBlockSource(level, false) : null; - } - - @Deprecated(since = "Zote") - public static NativeBlockSource getCurrentClientRegion() { - InnerCoreServer.useIncomprehensibleMethod("NativeBlockSource.getCurrentClientRegion()"); - return null; - } - - public static NativeBlockSource getFromCallbackPointer(Level level) { - return new NativeBlockSource(level, false); - } - - private static final NativeBlockSource serverCallbackBlockSource = new NativeBlockSource(null, false, null); - - public static NativeBlockSource getFromServerCallbackPointer(Level ptr) { - serverCallbackBlockSource.setNewPointer(ptr, false); - return serverCallbackBlockSource; - } - - public static void resetDefaultBlockSources() { - ICLog.d("BlockSource", "resetting default block sources"); - synchronized (defaultBlockSourceForDimensions) { - defaultBlockSourceForDimensions.clear(); - } - } - - private Level level; - - private boolean allowUpdate = true; - private boolean destroyParticles = true; - private int updateType = 3; - - public NativeBlockSource(Level level, boolean isFinalizable) { - if (level == null) { - throw new RuntimeException("cannot pass null pointer to NativeBlockSource constructor"); - } - this.level = level; - } - - private NativeBlockSource(Level level, boolean isFinalizable, Object nullablePointerTag) { - this.level = level; - } +import org.innercore.icstd.region.BlockSource; - public Level getPointer() { - return level; +// this is a stub class for DungeonUtility, it MUST NOT be used in code +@Deprecated +public class NativeBlockSource extends BlockSource { + public NativeBlockSource(long ptr, int refCntChange, Object stub) { + super((int) ptr, false, false); } - @ZoteOnly - public String getLevelName() { - return level.getName(); + public NativeBlockSource(long pointer, boolean isFinalizable) { + super(pointer, isFinalizable); } public NativeBlockSource(int dimension, boolean b1, boolean b2) { - this(BlockSourceMethods.getLevelForDimension(dimension), true); + super(dimension, b1, b2); } public NativeBlockSource(int dimension) { - this(dimension, true, false); - } - - private void setNewPointer(Level level, boolean isFinalizable) { - this.level = level; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - NativeBlockSource that = (NativeBlockSource) o; - return level == that.level; - } - - @Override - public int hashCode() { - return level.hashCode(); - } - - public void setBlockUpdateAllowed(boolean allowed) { - this.allowUpdate = allowed; - } - - public boolean getBlockUpdateAllowed() { - return allowUpdate; - } - - public void setBlockUpdateType(int type) { - this.updateType = type; - } - - public int getBlockUpdateType() { - return updateType; - } - - public void setDestroyParticlesEnabled(boolean enabled) { - destroyParticles = enabled; - } - - public boolean getDestroyParticlesEnabled() { - return destroyParticles; + super(dimension); } - public boolean canSeeSky(int x, int y, int z) { - return BlockSourceMethods.canSeeSky(level, x, y, z); - } - - public int getBiome(int x, int z) { - return BlockSourceMethods.getBiome(level, x, 64, z); - } - - public float getBiomeTemperatureAt(int x, int y, int z) { - return BlockSourceMethods.getBiomeTemperatureAt(level, x, y, z); - } - - public float getBiomeDownfallAt(int x, int y, int z) { - return BlockSourceMethods.getBiomeDownfallAt(level, x, y, z); - } - - public int getLightLevel(int x, int y, int z) { - return BlockSourceMethods.getBrightness(level, x, y, z); + public static NativeBlockSource getDefaultForDimension(int dimension) { + BlockSource region = BlockSource.getDefaultForDimension(dimension); + return region != null ? new NativeBlockSource(region.getDimension(), false) : null; } - public int getBlockId(int x, int y, int z) { - return BlockSourceMethods.getBlockId(level, x, y, z); + public static NativeBlockSource getDefaultForActor(long actor) { + BlockSource region = BlockSource.getDefaultForActor(actor); + return region != null ? new NativeBlockSource(region.level.getId(), false) : null; } - public int getBlockID(int x, int y, int z) { - return BlockSourceMethods.getBlockId(level, x, y, z); + public static NativeBlockSource getCurrentClientRegion() { + BlockSource region = BlockSource.getCurrentClientRegion(); + return region != null ? new NativeBlockSource(region.level.getId(), false) : null; } - public int getBlockData(int x, int y, int z) { - return BlockSourceMethods.getBlockData(level, x, y, z); + public static NativeBlockSource getCurrentWorldGenRegion() { + BlockSource region = BlockSource.getCurrentWorldGenRegion(); + return region != null ? new NativeBlockSource(region.level.getId(), false) : null; } public BlockState getBlock(int x, int y, int z) { - return new BlockState(BlockSourceMethods.getBlockIdDataAndState(level, x, y, z)); + return new BlockState(getBlockIdDataAndState(x, y, z)); } public BlockState getExtraBlock(int x, int y, int z) { - return new BlockState(BlockSourceMethods.getExtraBlockIdDataAndState(level, x, y, z)); - } - - public void setBlock(int x, int y, int z, int id, int data) { - BlockSourceMethods.setBlock(level, x, y, z, id, data, allowUpdate, updateType); - } - - public void setBlock(int x, int y, int z, int id) { - setBlock(x, y, z, id, 0); - } - - public void setBlock(int x, int y, int z, BlockState state) { - if (state.runtimeId > 0) { - BlockSourceMethods.setBlockByRuntimeId(level, x, y, z, state.runtimeId, allowUpdate, updateType); - } else { - BlockSourceMethods.setBlock(level, x, y, z, state.id, state.data, allowUpdate, updateType); - } - } - - public void setExtraBlock(int x, int y, int z, int id, int data) { - BlockSourceMethods.setExtraBlock(level, x, y, z, id, data, allowUpdate, updateType); - } - - public void setExtraBlock(int x, int y, int z, int id) { - setExtraBlock(x, y, z, id, 0); - } - - public void setExtraBlock(int x, int y, int z, BlockState state) { - if (state.runtimeId > 0) { - BlockSourceMethods.setExtraBlockByRuntimeId(level, x, y, z, state.runtimeId, allowUpdate, updateType); - } else { - BlockSourceMethods.setExtraBlock(level, x, y, z, state.id, state.data, allowUpdate, updateType); - } - } - - @Deprecated(since = "Zote") - public int getGrassColor(int x, int z) { - return BlockSourceMethods.getGrassColor(level, x, 64, z); - } - - public NativeTileEntity getBlockEntity(int x, int y, int z) { - BlockEntity ptr = BlockSourceMethods.getBlockEntity(level, x, y, z); - return ptr != null ? new NativeTileEntity(ptr) : null; - } - - public int getDimension() { - return BlockSourceMethods.getDimension(level); - } - - @ZoteOnly - public void setRespawnCoords(int x, int y, int z) { - BlockSourceMethods.setRespawnCoords(level, x, y, z); - } - - @ZoteOnly - public void playSound(float x, float y, float z, String name, float volume, float pitch, long[] players) { - BlockSourceMethods.playSound(level, name, (int) x, (int) y, (int) z, volume, pitch, players != null ? EntityMethod.getPlayersByIds(players) : null); - } - - private static long[] toLongArray(NativeArray array) { - LongArrayList result = new LongArrayList(); - for (Object obj : array.toArray()) { - try { - if (obj != null) - result.add((long) obj); - } catch (ClassCastException e) { - } - } - return result.toArray(new long[0]); - } - - @ZoteOnly - public void playSound(float x, float y, float z, String name, float volume, float pitch, NativeArray players) { - playSound(x, y, z, name, volume, pitch, players != null ? toLongArray(players) : null); - } - - @ZoteOnly - public void playSound(float x, float y, float z, String name, float volume, float pitch) { - playSound(x, y, z, name, volume, pitch, (long[]) null); - } - - public void setBiome(int chunkX, int chunkZ, int id) { - BlockSourceMethods.setBiome(level, chunkX, chunkZ, id); - } - - public boolean isChunkLoaded(int chunkX, int chunkZ) { - return BlockSourceMethods.isChunkLoaded(level, chunkX, chunkZ); - } - - public boolean isChunkLoadedAt(int x, int z) { - return BlockSourceMethods.isChunkLoaded(level, (int) Math.floor((double) x / 16), - (int) Math.floor((double) z / 16)); - } - - public int getChunkState(int chunkX, int chunkZ) { - return BlockSourceMethods.getChunkState(level, chunkX, chunkZ); - } - - public int getChunkStateAt(int x, int z) { - return BlockSourceMethods.getChunkState(level, (int) Math.floor((double) x / 16), - (int) Math.floor((double) z / 16)); - } - - public void addToTickingQueue(int x, int y, int z, BlockState block, int delay, int unknown) { - BlockSourceMethods.addToTickingQueue(level, x, y, z, block.getRuntimeId(), delay, unknown); - } - - public void addToTickingQueue(int x, int y, int z, BlockState block, int delay) { - addToTickingQueue(x, y, z, block, delay, 0); - } - - public void addToTickingQueue(int x, int y, int z, int delay, int unknown) { - BlockSourceMethods.addToTickingQueue(level, x, y, z, -1, delay, unknown); - } - - public void addToTickingQueue(int x, int y, int z, int delay) { - BlockSourceMethods.addToTickingQueue(level, x, y, z, -1, delay, 0); - } - - public float getRainLevel(){ - return BlockSourceMethods.getRainLevel(level); - } - - public void setRainLevel(float rainLevel){ - BlockSourceMethods.setRainLevel(level, rainLevel); - } - - public float getLightningLevel(){ - return BlockSourceMethods.getLightningLevel(level); - } - - public void setLightningLevel(float lightningLevel){ - BlockSourceMethods.setLightningLevel(level, lightningLevel); - } - - - public void destroyBlock(int x, int y, int z, boolean drop) { - BlockSourceMethods.destroyBlock(level, x, y, z, drop, updateType, destroyParticles); - } - - public void destroyBlock(int x, int y, int z) { - destroyBlock(x, y, z, false); - } - - public void breakBlock(int x, int y, int z, boolean isDropAllowed, long actor, ItemStack item) { - Coords coords = new Coords(x, y, z); - BlockState block = getBlock(x, y, z); - Callback.invokeAPICallback("BreakBlock", this, coords, block, isDropAllowed, actor, item.asScriptable()); - if (!NativeAPI.isDefaultPrevented() && StaticEntity.exists(actor) - && StaticEntity.getDimension(actor) == getDimension()) { - Callback.invokeAPICallback("DestroyBlock", coords, block, actor); - } - destroyBlock(x, y, z, isDropAllowed); - } - - public void breakBlock(int x, int y, int z, boolean isDropAllowed, long actor) { - breakBlock(x, y, z, isDropAllowed, actor, StaticEntity.getCarriedItem(actor)); - } - - public void breakBlock(int x, int y, int z, boolean isDropAllowed, ItemStack item) { - breakBlock(x, y, z, isDropAllowed, -1, item); - } - - public void breakBlock(int x, int y, int z, boolean isDropAllowed) { - breakBlock(x, y, z, isDropAllowed, -1, new ItemStack()); - } - - private static final Object breakBlockSyncLock = new Object(); - - public BlockBreakResult breakBlockForResult(int x, int y, int z, long actor, ItemStack item) { - synchronized (breakBlockSyncLock) { - NativeCallback.startOverrideBlockBreakResult(); - breakBlock(x, y, z, true, actor, item); - return NativeCallback.endOverrideBlockBreakResult(); - } - } - - public BlockBreakResult breakBlockForResult(int x, int y, int z, long actor) { - return breakBlockForResult(x, y, z, actor, StaticEntity.getCarriedItem(actor)); - } - - public BlockBreakResult breakBlockForResult(int x, int y, int z, ItemStack item) { - return breakBlockForResult(x, y, z, -1, item); - } - - public BlockBreakResult breakBlockForResult(int x, int y, int z) { - return breakBlockForResult(x, y, z, -1, new ItemStack()); - } - - public ScriptableObject breakBlockForJsResult(int x, int y, int z, long actor, ScriptableObject item) { - return breakBlockForResult(x, y, z, actor, new ItemStack(item)).asScriptable(); - } - - public ScriptableObject breakBlockForJsResult(int x, int y, int z, long actor) { - return breakBlockForResult(x, y, z, actor).asScriptable(); - } - - public ScriptableObject breakBlockForJsResult(int x, int y, int z, ScriptableObject item) { - return breakBlockForResult(x, y, z, new ItemStack(item)).asScriptable(); - } - - public ScriptableObject breakBlockForJsResult(int x, int y, int z) { - return breakBlockForResult(x, y, z).asScriptable(); - } - - public void explode(float x, float y, float z, float power, boolean fire) { - BlockSourceMethods.explode(level, x, y, z, power, fire); - } - - public long clip(float x1, float y1, float z1, float x2, float y2, float z2, int mode, float[] output) { - return BlockSourceMethods.clip(level, x1, y1, z1, x2, y2, z2, mode, output); - } - - public long[] fetchEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - int backCompEntityType, boolean flag) { - return BlockSourceMethods.fetchEntitiesInAABB(level, x1, y1, z1, x2, y2, z2, backCompEntityType, flag); - } - - public long[] fetchEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - int backCompEntityType) { - return fetchEntitiesInAABB(x1, y1, z1, x2, y2, z2, backCompEntityType, false); - } - - public long[] fetchEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2) { - return fetchEntitiesInAABB(x1, y1, z1, x2, y2, z2, 256, false); - } - - public long[] fetchEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - String namespace, String type) { - return BlockSourceMethods.fetchEntitiesOfTypeInAABB(level, x1, y1, z1, x2, y2, z2, namespace, type); - } - - public long[] fetchEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - String fullType) { - String namespace = "minecraft"; - int sep = fullType.indexOf(':'); - if (sep != -1) { - namespace = fullType.substring(0, sep); - fullType = fullType.substring(sep + 1); - } - return fetchEntitiesOfTypeInAABB(x1, y1, z1, x2, y2, z2, namespace, fullType); - } - - private static NativeArray entityListToScriptArray(long[] ents) { - Object[] result = new Object[ents.length]; - int i = 0; - for (long ent : ents) { - result[i++] = ent; - } - return ScriptableObjectHelper.createArray(result); - } - - public NativeArray listEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - int backCompEntityType, boolean flag) { - return entityListToScriptArray(fetchEntitiesInAABB(x1, y1, z1, x2, y2, z2, backCompEntityType, flag)); - } - - public NativeArray listEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - int backCompEntityType) { - return listEntitiesInAABB(x1, y1, z1, x2, y2, z2, backCompEntityType, false); - } - - public NativeArray listEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2) { - return listEntitiesInAABB(x1, y1, z1, x2, y2, z2, 256, false); - } - - public NativeArray listEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - String namespace, String type) { - return entityListToScriptArray(fetchEntitiesOfTypeInAABB(x1, y1, z1, x2, y2, z2, namespace, type)); - } - - public NativeArray listEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, - String fullType) { - return entityListToScriptArray(fetchEntitiesOfTypeInAABB(x1, y1, z1, x2, y2, z2, fullType)); - } - - public long spawnEntity(float x, float y, float z, int type) { - return BlockSourceMethods.spawnEntity(level, type, x, y, z); - } - - public long spawnEntity(float x, float y, float z, String str1, String str2, String str3) { - return BlockSourceMethods.spawnNamespacedEntity(level, x, y, z, str1, str2, str3); - } - - public int getRedstoneSignal(int x, int y, int z) { - return getRedstoneSignal(level, x, y, z); - } - - public void setRedstoneSignal(int x, int y, int z, int level, int delay, int facing) { - setRedstoneSignal(this.level, x, y, z, level, delay, facing); - } - - public void setRedstoneSignal(int x, int y, int z, int level, int delay) { - setRedstoneSignal(x, y, z, level, delay, -1); - } - - public void setRedstoneSignal(int x, int y, int z, int level) { - setRedstoneSignal(x, y, z, level, 4); - } - - public void randomTick(int x, int y, int z) { - BlockSourceMethods.randomTick(level, x , y, z); - } - - public long spawnEntity(float x, float y, float z, String type) { - if (type == null || type.length() == 0) { - return -1; - } - String[] parts = type.split(":"); - if (parts.length >= 3) { - return spawnEntity(x, y, z, parts[0], parts[1], parts[2]); - } else if (parts.length == 2) { - return spawnEntity(x, y, z, parts[0], parts[1], ""); - } else if (parts.length == 1) { - return spawnEntity(x, y, z, "minecraft", parts[0], ""); - } - return -1; - } - - public long spawnDroppedItem(float x, float y, float z, int id, int count, int data, - NativeItemInstanceExtra extra) { - return BlockSourceMethods.spawnDroppedItem(level, x, y, z, id, count, data, extra); - } - - public long spawnDroppedItem(float x, float y, float z, int id, int count, int data) { - return spawnDroppedItem(x, y, z, id, count, data, null); - } - - public void spawnExpOrbs(float x, float y, float z, int amount) { - BlockSourceMethods.spawnExpOrbs(level, x, y, z, amount); - } - - private static int getRedstoneSignal(Level pointer, int x, int y, int z) { - return pointer.getStrongPower(new Vector3(x, y, z)); - } - - private static void setRedstoneSignal(Level pointer, int x, int y, int z, int level, int delay, int facing) { - InnerCoreServer.useNotCurrentSupport("NativeBlockSource.setRedstoneSignal"); + return new BlockState(getExtraBlockIdDataAndState(x, y, z)); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativePlayer.java b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativePlayer.java index 80c48bd60..7613ed4e4 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativePlayer.java +++ b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativePlayer.java @@ -5,8 +5,8 @@ import com.reider745.api.ZoteOnly; import com.reider745.entity.EntityMethod; import com.reider745.entity.PlayerActorMethods; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.NativeScales; public class NativePlayer { @@ -58,15 +58,15 @@ public boolean isValid() { return PlayerActorMethods.isValid(player, true); } - public void invokeUseItemNoTarget(int id, int count, int data, NativeItemInstanceExtra extra) { + public void invokeUseItemNoTarget(int id, int count, int data, ItemInstanceExtra extra) { PlayerActorMethods.invokeUseItemNoTarget(player, id, count, data, extra); } - public void addItemToInventory(int id, int count, int data, NativeItemInstanceExtra extra, boolean dropLeft) { + public void addItemToInventory(int id, int count, int data, ItemInstanceExtra extra, boolean dropLeft) { PlayerActorMethods.addItemToInventory(player, id, count, data, extra, dropLeft); } - public void addItemToInventory(int id, int count, int data, NativeItemInstanceExtra extra) { + public void addItemToInventory(int id, int count, int data, ItemInstanceExtra extra) { addItemToInventory(id, count, data, extra, true); } @@ -90,12 +90,12 @@ public int getGameMode() { return PlayerActorMethods.getGameMode(player); } - public ItemStack getInventorySlot(int slot) { - return ItemStack.fromPtr(PlayerActorMethods.getInventorySlot(player, slot)); + public ItemInstance getInventorySlot(int slot) { + return ItemInstance.makeFrom(PlayerActorMethods.getInventorySlot(player, slot)); } - public ItemStack getArmor(int slot) { - return ItemStack.fromPtr(PlayerActorMethods.getArmor(player, slot)); + public ItemInstance getArmor(int slot) { + return ItemInstance.makeFrom(PlayerActorMethods.getArmor(player, slot)); } public float getExhaustion() { @@ -126,11 +126,11 @@ public int getSelectedSlot() { return PlayerActorMethods.getSelectedSlot(player); } - public void setInventorySlot(int slot, int id, int count, int data, NativeItemInstanceExtra extra) { + public void setInventorySlot(int slot, int id, int count, int data, ItemInstanceExtra extra) { PlayerActorMethods.setInventorySlot(player, slot, id, count, data, extra); } - public void setArmor(int slot, int id, int count, int data, NativeItemInstanceExtra extra) { + public void setArmor(int slot, int id, int count, int data, ItemInstanceExtra extra) { PlayerActorMethods.setArmor(player, slot, id, count, data, extra); } diff --git a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbench.java b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbench.java index b3a2ceb16..bc98bd96f 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbench.java +++ b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbench.java @@ -12,11 +12,10 @@ import cn.nukkit.recipe.impl.ShapedRecipe; import cn.nukkit.recipe.impl.ShapelessRecipe; import com.reider745.InnerCoreServer; -import com.reider745.api.ReflectHelper; import com.reider745.hooks.item.ItemUtils; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; import java.util.*; @@ -65,14 +64,14 @@ private static class RecipeRegistryEntry implements IApply { public int resultId; public int resultCount; public int resultData; - public NativeItemInstanceExtra resultExtra; + public ItemInstanceExtra resultExtra; public String[] pattern; - public HashMap ingredients; + public HashMap ingredients; public int[] ingredients_; public boolean shapeless = false; - public RecipeRegistryEntry(int resultId, int resultCount, int resultData, NativeItemInstanceExtra resultExtra, String[] pattern, - HashMap ingredients) { + public RecipeRegistryEntry(int resultId, int resultCount, int resultData, ItemInstanceExtra resultExtra, String[] pattern, + HashMap ingredients) { this.resultId = resultId; this.resultCount = resultCount; this.resultData = resultData; @@ -81,7 +80,7 @@ public RecipeRegistryEntry(int resultId, int resultCount, int resultData, Native this.ingredients = ingredients; } - public RecipeRegistryEntry(int resultId, int resultCount, int resultData, NativeItemInstanceExtra resultExtra, int[] ingredients) { + public RecipeRegistryEntry(int resultId, int resultCount, int resultData, ItemInstanceExtra resultExtra, int[] ingredients) { this.resultId = resultId; this.resultCount = resultCount; this.resultData = resultData; @@ -105,8 +104,8 @@ public IApply apply(RuntimeItemMapping mapping) { Item item; if (!this.shapeless) { final Map ingredients = new HashMap<>(); - for (Map.Entry ingredient : this.ingredients.entrySet()) { - ItemStack stack = ingredient.getValue(); + for (Map.Entry ingredient : this.ingredients.entrySet()) { + ItemInstance stack = ingredient.getValue(); item = ItemUtils.get(stack.id, stack.data); try { mapping.toRuntime(item.getId(), item.getDamage()); @@ -158,20 +157,20 @@ public IApply apply(RuntimeItemMapping mapping) { private static final ArrayList recipes = new ArrayList<>(); private static RuntimeItemMapping runtimeItemMapping = null; - private static void addShapedRecipe(int resultId, int resultCount, int resultData, NativeItemInstanceExtra resultExtra, + private static void addShapedRecipe(int resultId, int resultCount, int resultData, ItemInstanceExtra resultExtra, String[] pattern, int[] ingredients) { if (resultId == 0) { Logger.error("Recipe result id 0"); return; } - final HashMap ingredients_ = new HashMap<>(); + final HashMap ingredients_ = new HashMap<>(); for (int i = 0; i < ingredients.length; i += 3) { int id = ingredients[i + 1]; if (id == 0) { Logger.error("Recipe result id 0"); return; } - ingredients_.put((char) ingredients[i], new ItemStack(id, 1, ingredients[i + 2])); + ingredients_.put((char) ingredients[i], new ItemInstance(id, 1, ingredients[i + 2])); } final RecipeRegistryEntry recipe = new RecipeRegistryEntry(resultId, resultCount, resultData, resultExtra, pattern, ingredients_); @@ -184,11 +183,11 @@ private static void addShapedRecipe(int resultId, int resultCount, int resultDat } } - public static void addShapedRecipe(ItemStack result, String[] pattern, int[] ingredients) { + public static void addShapedRecipe(ItemInstance result, String[] pattern, int[] ingredients) { addShapedRecipe(result.id, result.count, result.data, result.extra, pattern, ingredients); } - private static void addShapelessRecipe(int resultId, int resultCount, int resultData, NativeItemInstanceExtra resultExtra, + private static void addShapelessRecipe(int resultId, int resultCount, int resultData, ItemInstanceExtra resultExtra, int[] ingredients) { if (resultId == 0) { Logger.error("Recipe result id 0"); @@ -200,7 +199,7 @@ private static void addShapelessRecipe(int resultId, int resultCount, int result recipe.rebuildPacket(); } - public static void addShapelessRecipe(ItemStack result, int[] ingredients) { + public static void addShapelessRecipe(ItemInstance result, int[] ingredients) { addShapelessRecipe(result.id, result.count, result.data, result.extra, ingredients); } diff --git a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainer.java b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainer.java index b4ebdacf6..33d2d59e6 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainer.java +++ b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainer.java @@ -1,12 +1,11 @@ package com.zhekasmirnov.apparatus.mcpe; import com.reider745.hooks.item.ItemUtils; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.apparatus.api.container.ItemContainerSlot; import com.zhekasmirnov.apparatus.util.Java8BackComp; import com.zhekasmirnov.innercore.api.NativeItemInstance; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; +import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchField; import com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchRecipe; @@ -37,12 +36,12 @@ public NativeWorkbenchContainer(CraftingGrid pointer, int size, long owningPlaye // basic slot logic - ItemStack getSlot(int i) { + ItemInstance getSlot(int i) { NativeWorkbenchContainerSlot slot = i >= 0 && i < slots.length ? this.slots[i] : null; - return slot != null ? new ItemStack(slot.id, slot.count, slot.data, slot.extra) : new ItemStack(); + return slot != null ? new org.innercore.icstd.inventory.ItemInstance(slot.id, slot.count, slot.data, slot.extra) : new ItemInstance(); } - void setSlot(int i, ItemStack item) { + void setSlot(int i, ItemInstance item) { NativeWorkbenchContainerSlot slot = i >= 0 && i < slots.length ? this.slots[i] : null; if (slot != null) { slot.set(item); @@ -51,7 +50,7 @@ void setSlot(int i, ItemStack item) { public void updateSlots() { for (int i = 0; i < slots.length; i++) { - slots[i] = new NativeWorkbenchContainerSlot(new NativeItemInstance(pointer.getItem(i))); + slots[i] = new NativeWorkbenchContainerSlot(getSlot(i)); } } @@ -65,12 +64,12 @@ public void apply() { } @Override - public ItemContainerSlot getFieldSlot(int slot) { + public AbstractSlot getFieldSlot(int slot) { return slot >= 0 && slot < slots.length ? this.slots[slot] : Java8BackComp.getOrDefault(slotPlaceholders, slot, new NativeWorkbenchContainerSlot()); } @Override - public ItemContainerSlot getFieldSlot(int x, int y) { + public AbstractSlot getFieldSlot(int x, int y) { if (x >= 0 && y >= 0 && x < size && y < size) { return getFieldSlot(y * size + x); } else { @@ -107,7 +106,7 @@ public NativeWorkbenchContainerSlot getSlot(String name) { return null; } - public void setSlot(String name, int id, int count, int data, NativeItemInstanceExtra extra) { + public void setSlot(String name, int id, int count, int data, ItemInstanceExtra extra) { NativeWorkbenchContainerSlot slot = getSlot(name); if (slot != null) { slot.set(id, count, data, extra); @@ -133,27 +132,27 @@ public WorkbenchRecipe getRecipe() { return getRecipe(""); } - public ItemStack getPreviewResult(String prefix) { + public ItemInstance getPreviewResult(String prefix) { WorkbenchRecipe recipe = getRecipe(prefix); if (recipe != null) { ItemInstance result = recipe.getResult(); if (result != null) { - return new ItemStack(result); + return new ItemInstance(result); } } return null; } - public ItemStack getPreviewResult() { + public ItemInstance getPreviewResult() { return getPreviewResult(""); } - public ItemStack provideRecipe(String prefix) { + public ItemInstance provideRecipe(String prefix) { ItemInstance result = WorkbenchRecipeRegistry.provideRecipeForPlayer(this, prefix, owningPlayer); - return result != null ? new ItemStack(result) : null; + return result != null ? new ItemInstance(result) : null; } - public ItemStack provideRecipe() { + public ItemInstance provideRecipe() { return provideRecipe(""); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainerSlot.java b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainerSlot.java index fb3d3ef3c..fff23c276 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainerSlot.java +++ b/src/main/java/com/zhekasmirnov/apparatus/mcpe/NativeWorkbenchContainerSlot.java @@ -1,17 +1,14 @@ package com.zhekasmirnov.apparatus.mcpe; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.apparatus.api.container.ItemContainerSlot; -import com.zhekasmirnov.innercore.api.NativeItemInstance; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; +import org.innercore.icstd.inventory.ItemInstance; -import cn.nukkit.item.Item; - -public class NativeWorkbenchContainerSlot extends ItemContainerSlot { +public class NativeWorkbenchContainerSlot implements AbstractSlot { public int id, count, data; - public NativeItemInstanceExtra extra; + public ItemInstanceExtra extra; - public NativeWorkbenchContainerSlot(int id, int count, int data, NativeItemInstanceExtra extra) { + public NativeWorkbenchContainerSlot(int id, int count, int data, ItemInstanceExtra extra) { this.id = id; this.count = count; this.data = data; @@ -22,11 +19,7 @@ public NativeWorkbenchContainerSlot(int id, int count, int data, NativeItemInsta this(0, 0, 0, null); } - public NativeWorkbenchContainerSlot(ItemStack item) { - this(item.id, item.count, item.data, item.extra); - } - - public NativeWorkbenchContainerSlot(NativeItemInstance item) { + public NativeWorkbenchContainerSlot(ItemInstance item) { this(item.id, item.count, item.data, item.extra); } @@ -46,27 +39,23 @@ public int getData() { } @Override - public NativeItemInstanceExtra getExtra() { + public ItemInstanceExtra getExtra() { return extra; } - public Item getExtraPtr() { - return NativeItemInstanceExtra.getValueOrNullPtr(extra); + public long getExtraPtr() { + return ItemInstanceExtra.getValueOrNullPtr(extra); } @Override - public void set(int id, int count, int data, NativeItemInstanceExtra extra) { + public void set(int id, int count, int data, ItemInstanceExtra extra) { this.id = id; this.count = count; this.data = data; this.extra = extra; } - public void set(ItemStack item) { - set(item.id, item.count, item.data, item.extra); - } - - public void set(NativeItemInstance item) { + public void set(ItemInstance item) { set(item.id, item.count, item.data, item.extra); } diff --git a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ConnectedClient.java b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ConnectedClient.java index 05dd8ec9d..d27cd8ba3 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ConnectedClient.java +++ b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ConnectedClient.java @@ -10,6 +10,7 @@ import com.zhekasmirnov.apparatus.util.Java8BackComp; import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.api.log.ICLog; +import org.innercore.icstd.network.NetworkClient; import java.io.IOException; import java.util.*; @@ -39,6 +40,8 @@ public enum ClientState { private final Set remainingInitializationPackets = new HashSet<>(); private InitializationPacketException initializationPacketFailureCause = null; + public final NetworkClient apiClient = new NetworkClient(this); + public interface InitializationPacketListener { void onPacketReceived(ConnectedClient client, Object data, Class dataType) throws InitializationPacketException; diff --git a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ModdedServer.java b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ModdedServer.java index 4cb23907b..20962696a 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ModdedServer.java +++ b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/server/ModdedServer.java @@ -11,6 +11,8 @@ import com.zhekasmirnov.apparatus.multiplayer.socket.SocketServer; import com.zhekasmirnov.apparatus.util.Java8BackComp; import com.zhekasmirnov.horizon.runtime.logger.Logger; +import org.innercore.icstd.network.NetworkClient; +import org.innercore.icstd.network.NetworkModule; import java.io.IOException; import java.net.Socket; @@ -25,6 +27,7 @@ public class ModdedServer implements SocketServer.IClientConnectListener, Native private final List initializingClients = new ArrayList<>(); private final List connectedClients = new ArrayList<>(); + private final List connectedClientsForApi = new ArrayList<>(); private final Map connectedClientByPlayerUid = new HashMap<>(); private final Object networkThreadQueueMonitor = new Object(); @@ -84,6 +87,10 @@ public List getConnectedClients() { return new ArrayList<>(connectedClients); } + public List getConnectedClientsForApi() { + return connectedClientsForApi; + } + public ConnectedClient getConnectedClientForPlayer(long player) { return connectedClientByPlayerUid.get(player); } @@ -174,6 +181,8 @@ public void onClientConnected(DataChannel channel, RakNetPlayerSessionHooks cl) } }else if(InnerCoreServer.isDebugInnerCoreNetwork()) System.out.println("Not listeners, received packet player=" + client.getPlayerUid() + " name="+name); + + NetworkModule.processPacketImpl(client.apiClient, name, data, meta); }); client.start(); @@ -243,6 +252,7 @@ public void onStateChanged(ConnectedClient client, ConnectedClient.ClientState n synchronized (connectedClients) { initializingClients.remove(client); connectedClients.add(client); + connectedClientsForApi.add(client.apiClient); connectedClientByPlayerUid.put(client.getPlayerUid(), client); } break; @@ -251,6 +261,7 @@ public void onStateChanged(ConnectedClient client, ConnectedClient.ClientState n synchronized (connectedClients) { initializingClients.remove(client); connectedClients.remove(client); + connectedClientsForApi.remove(client.apiClient); connectedClientByPlayerUid.remove(client.getPlayerUid()); } break; diff --git a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/NetworkEntity.java b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/NetworkEntity.java index 999f4bd38..dcc580018 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/NetworkEntity.java +++ b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/NetworkEntity.java @@ -146,7 +146,7 @@ public interface IDisconnectionPlayer { private boolean isRemoved = false; - private final ModdedClient clientInstance = Network.getSingleton().getClient(); + //private final ModdedClient clientInstance = Network.getSingleton().getClient(); private final ConnectedClientList clients = new ConnectedClientList(); private JobExecutor clientExecutor = Network.getSingleton().getClientThreadJobExecutor(); @@ -264,13 +264,8 @@ public void send(String name, Object data) { if (isRemoved) { return; } - if (isServer) { - ThreadTypeMarker.assertServerThread(); - clients.send("system.entity.packet#" + this.name + "#" + name, data); - } else { - ThreadTypeMarker.assertClientThread(); - clientInstance.send("system.entity.packet#" + this.name + "#" + name, data); - } + ThreadTypeMarker.assertServerThread(); + clients.send("system.entity.packet#" + this.name + "#" + name, data); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/SyncedNetworkData.java b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/SyncedNetworkData.java index 3d953ac78..b028e71a2 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/SyncedNetworkData.java +++ b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/entity/SyncedNetworkData.java @@ -96,7 +96,7 @@ public interface OnDataChangedListener { private final String name; private final boolean isServer; - private final ModdedClient clientInstance = Network.getSingleton().getClient(); + //private final ModdedClient clientInstance = Network.getSingleton().getClient(); private ConnectedClientList clients = new ConnectedClientList(); private final Map data = new HashMap<>(); @@ -176,13 +176,8 @@ protected void put(String key, Object val, boolean isExternal) { public void apply() { if (!dirtyData.isEmpty()) { synchronized (dirtyData) { - if (isServer) { - ThreadTypeMarker.assertServerThread(); - clients.send("system.synced_data.data#" + name, new JSONObject(dirtyData)); - } else { - ThreadTypeMarker.assertClientThread(); - clientInstance.send("system.synced_data.data#" + name, new JSONObject(dirtyData)); - } + ThreadTypeMarker.assertServerThread(); + clients.send("system.synced_data.data#" + name, new JSONObject(dirtyData)); dirtyData.clear(); } } diff --git a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/list/ConnectedClientList.java b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/list/ConnectedClientList.java index ee125cb74..526b7879c 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/list/ConnectedClientList.java +++ b/src/main/java/com/zhekasmirnov/apparatus/multiplayer/util/list/ConnectedClientList.java @@ -307,4 +307,8 @@ public Set getClientCollection() { refresh(); return clients; } + + public Set getClientCollectionsNoRefresh() { + return clients; + } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeAPI.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeAPI.java index a360d9b51..815f628ae 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeAPI.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeAPI.java @@ -19,6 +19,8 @@ import com.zhekasmirnov.innercore.api.constants.PlayerAbility; import com.zhekasmirnov.innercore.api.runtime.LevelInfo; import com.zhekasmirnov.innercore.mod.resource.ResourceStorage; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.innercore.icstd.region.BlockSource; public class NativeAPI { @@ -123,26 +125,27 @@ public static void setBehaviorPacksPathOverride(String path) { } public static void invokeUseItemNoTarget(int id, int count, int data, Item extra) { - invokeUseItemNoTarget(id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + invokeUseItemNoTarget(id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void invokeUseItemNoTarget(int id, int count, int data, NativeItemInstanceExtra extra) { + public static void invokeUseItemNoTarget(int id, int count, int data, ItemInstanceExtra extra) { InnerCoreServer.useNotCurrentSupport("NativeAPI.invokeUseItemNoTarget(id, count, data, extra)"); } public static void invokeUseItemOn(int id, int count, int data, Item extra, int x, int y, int z, int side, float vx, float vy, float vz, long entity) { - invokeUseItemOn(id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra), x, y, z, side, vx, vy, vz, entity); + invokeUseItemOn(id, count, data, ItemInstanceExtra.getExtraOrNull(extra), x, y, z, side, vx, vy, vz, entity); } - public static void invokeUseItemOn(int id, int count, int data, NativeItemInstanceExtra extra, int x, int y, int z, int side, float vx, + public static void invokeUseItemOn(int id, int count, int data, ItemInstanceExtra extra, int x, int y, int z, int side, float vx, float vy, float vz, long entity) { EntityMethod.invokeUseItemOn(id, count, data, extra, x, y, z, side, vx, vy, vz, entity); } @Deprecated(since = "Zote") + @ClientOnly public static void playSound(String name, float x, float y, float z, float f1, float f2) { - NativeBlockSource region = NativeBlockSource.getDefault(); + BlockSource region = BlockSource.getDefault(); if (region != null) region.playSound(x, y, z, name, f1, f2); InnerCoreServer.useClientMethod("NativeAPI.playSound(name, x, y, z, f1, f2)"); @@ -348,12 +351,14 @@ public static long[] fetchEntitiesInAABB(float x1, float y1, float z1, float x2, return new long[0]; } + @Deprecated public static long getEntityArmor(long entity, int slot) { InnerCoreServer.useNotCurrentSupport("NativeAPI.getEntityArmor(entity, slot)"); // return EntityMethod.getEntityArmor(entity, slot); return 0; } + @Deprecated public static long getEntityCarriedItem(long entity) { InnerCoreServer.useNotCurrentSupport("NativeAPI.getEntityCarriedItem(entity)"); // return EntityMethod.getEntityCarriedItem(entity); @@ -576,14 +581,14 @@ public static boolean isDefaultPrevented() { } public static boolean isGlintItemInstance(int id, int data) { - return isGlintItemInstance(id, data, (NativeItemInstanceExtra) null); + return isGlintItemInstance(id, data, (ItemInstanceExtra) null); } public static boolean isGlintItemInstance(int id, int data, Item extra) { - return isGlintItemInstance(id, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + return isGlintItemInstance(id, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static boolean isGlintItemInstance(int id, int data, NativeItemInstanceExtra extra) { + public static boolean isGlintItemInstance(int id, int data, ItemInstanceExtra extra) { InnerCoreServer.useNotCurrentSupport("NativeAPI.isGlintItemInstance(id, data, extra)"); return false; } @@ -703,26 +708,26 @@ public static void setDifficulty(int difficulty) { } public static void setEntityArmor(long entity, int slot, int id, int count, int data, Item extra) { - setEntityArmor(entity, slot, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setEntityArmor(entity, slot, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setEntityArmor(long entity, int slot, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setEntityArmor(long entity, int slot, int id, int count, int data, ItemInstanceExtra extra) { EntityMethod.setEntityArmor(entity, slot, id, count, data, extra); } public static void setEntityCarriedItem(long entity, int id, int count, int data, Item extra) { - setEntityCarriedItem(entity, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setEntityCarriedItem(entity, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setEntityCarriedItem(long entity, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setEntityCarriedItem(long entity, int id, int count, int data, ItemInstanceExtra extra) { EntityMethod.setEntityCarriedItem(entity, id, count, data, extra); } public static void setEntityOffhandItem(long entity, int id, int count, int data, Item extra) { - setEntityOffhandItem(entity, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setEntityOffhandItem(entity, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setEntityOffhandItem(long entity, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setEntityOffhandItem(long entity, int id, int count, int data, ItemInstanceExtra extra) { EntityMethod.setEntityOffhandItem(entity, id, count, data, extra); } @@ -760,10 +765,10 @@ public static void setItemRequiresIconOverride(int id, boolean enabled) { } public static void setItemToDrop(long entity, int id, int count, int data, Item extra) { - setItemToDrop(entity, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setItemToDrop(entity, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setItemToDrop(long entity, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setItemToDrop(long entity, int id, int count, int data, ItemInstanceExtra extra) { EntityMethod.setItemToDrop(entity, id, count, data, extra); } @@ -854,7 +859,7 @@ public static void setRenderType(long entity, int type) { } public static void setRespawnCoords(int x, int y, int z) { - NativeBlockSource region = NativeBlockSource.getDefault(); + BlockSource region = BlockSource.getDefault(); if (region != null) region.setRespawnCoords(x, y, z); } @@ -977,11 +982,11 @@ public static void setVelocityAxis(long entity, int axis, float value) { @Deprecated(since = "Zote") public static long spawnDroppedItem(float x, float y, float z, int id, int count, int data, Item extra) { - return spawnDroppedItem(x, y, z, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + return spawnDroppedItem(x, y, z, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } @Deprecated(since = "Zote") - public static long spawnDroppedItem(float x, float y, float z, int id, int count, int data, NativeItemInstanceExtra extra) { + public static long spawnDroppedItem(float x, float y, float z, int id, int count, int data, ItemInstanceExtra extra) { NativeBlockSource region = NativeBlockSource.getCurrentWorldGenRegion(); if (region != null) return region.spawnDroppedItem(x, y, z, id, count, data, extra); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeBlock.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeBlock.java index be756da54..32c6e9dd4 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeBlock.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeBlock.java @@ -10,10 +10,10 @@ import com.zhekasmirnov.apparatus.ecs.core.EntityManager; import com.zhekasmirnov.apparatus.ecs.types.ECSTags; import com.zhekasmirnov.apparatus.ecs.types.block.BlockComponent; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; import com.zhekasmirnov.innercore.api.runtime.other.NameTranslation; import com.zhekasmirnov.innercore.api.unlimited.BlockShape; import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.innercore.icstd.region.BlockSource; import org.mozilla.javascript.Function; import java.util.ArrayList; @@ -357,7 +357,7 @@ public void onIdDataIterated(int id, int data) { } } - public static void onRandomTickCallback(int x, int y, int z, int id, int data, NativeBlockSource blockSource) { + public static void onRandomTickCallback(int x, int y, int z, int id, int data, BlockSource blockSource) { Function callback = tickingCallbackForId.get(id); if (callback != null) { callback.call(Compiler.assureContextForCurrentThread(), callback.getParentScope(), diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeBlockRenderer.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeBlockRenderer.java index 9f1455b1c..be0e342d9 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeBlockRenderer.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeBlockRenderer.java @@ -1,13 +1,14 @@ package com.zhekasmirnov.innercore.api; import com.reider745.InnerCoreServer; -import com.zhekasmirnov.innercore.api.commontypes.Coords; -import com.zhekasmirnov.innercore.api.commontypes.FullBlock; +import com.reider745.api.ClientOnly; import com.zhekasmirnov.innercore.api.constants.ChatColor; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.runtime.Callback; import com.zhekasmirnov.innercore.api.runtime.LevelInfo; import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.innercore.icstd.common.script.GenericScriptPos3; +import org.innercore.icstd.region.BlockState; import org.mozilla.javascript.Function; import org.mozilla.javascript.annotations.JSStaticFunction; @@ -252,21 +253,21 @@ public static void _addRenderCallback(int id, Function callback) { } @Deprecated(since = "Zote") - public static void onRenderCall(RenderAPI api, Coords coords, FullBlock block, boolean b) { + @ClientOnly + public static void onRenderCall(RenderAPI api, GenericScriptPos3 coords, BlockState block, boolean b) { Callback.invokeAPICallback("CustomBlockTessellation", api, coords, block, b); Function callback = jsRenderCallbacks.get(block.id); if (callback != null) { try { - callback.call(Compiler.assureContextForCurrentThread(), callback.getParentScope(), - callback.getParentScope(), new Object[] { api, coords, block, b }); + callback.call(Compiler.assureContextForCurrentThread(), callback.getParentScope(), callback.getParentScope(), new Object[]{api, coords, block, b}); } catch (Exception e) { - ICLog.e("TESSELLATION", "error occurred in block tessellation id=" + block.id + " data=" + block.data, - e); + ICLog.e("TESSELLATION", "error occurred in block tessellation id=" + block.id + " data=" + block.data, e); if (LevelInfo.isLoaded()) { NativeAPI.clientMessage(ChatColor.RED + "error occurred in block tessellation."); } } } + } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeCallback.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeCallback.java index 0fb5502fc..05e301c46 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeCallback.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeCallback.java @@ -13,34 +13,29 @@ import com.zhekasmirnov.apparatus.adapter.innercore.EngineConfig; import com.zhekasmirnov.apparatus.adapter.innercore.game.Minecraft; import com.zhekasmirnov.apparatus.adapter.innercore.game.block.BlockBreakResult; -import com.zhekasmirnov.apparatus.adapter.innercore.game.block.BlockState; import com.zhekasmirnov.apparatus.adapter.innercore.game.entity.StaticEntity; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; import com.zhekasmirnov.apparatus.ecs.ECS; import com.zhekasmirnov.apparatus.ecs.types.ServerTicking; import com.zhekasmirnov.apparatus.mcpe.NativePlayer; import com.zhekasmirnov.apparatus.mcpe.NativeWorkbenchContainer; import com.zhekasmirnov.apparatus.minecraft.enums.GameEnums; import com.zhekasmirnov.apparatus.api.player.NetworkPlayerRegistry; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; import com.zhekasmirnov.apparatus.mcpe.NativeNetworking; import com.zhekasmirnov.apparatus.multiplayer.Network; import com.zhekasmirnov.apparatus.multiplayer.ThreadTypeMarker; import com.zhekasmirnov.apparatus.multiplayer.server.ConnectedClient; import com.zhekasmirnov.apparatus.ecs.core.EntitySystem; -import com.zhekasmirnov.apparatus.multiplayer.util.list.ConnectedClientList; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.commontypes.Coords; -import com.zhekasmirnov.innercore.api.commontypes.FullBlock; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; import com.zhekasmirnov.innercore.api.commontypes.ScriptableParams; import com.zhekasmirnov.innercore.api.entities.NativePathNavigation; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.adaptedscript.AdaptedScriptAPI.IDRegistry; import com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchRecipe; import com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchRecipeRegistry; +import com.zhekasmirnov.innercore.api.mod.util.InventorySource; import com.zhekasmirnov.innercore.api.mod.util.ScriptableFunctionImpl; import com.zhekasmirnov.innercore.api.runtime.*; +import com.zhekasmirnov.innercore.api.runtime.other.ArmorRegistry; import com.zhekasmirnov.innercore.api.runtime.other.NameTranslation; import com.zhekasmirnov.innercore.api.runtime.other.WorldGen; import com.zhekasmirnov.innercore.api.runtime.saver.world.WorldDataSaverHandler; @@ -49,6 +44,14 @@ import java.util.*; import java.util.function.Consumer; +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.common.script.GenericScriptPos3; +import org.innercore.icstd.event.Events; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.region.BlockState; +import org.innercore.icstd.scene.GameObjectManager; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; @@ -61,6 +64,25 @@ public class NativeCallback { private static final Object[] EMPTY_ARGS = new Object[0]; + // Global generation, that increments on world load/leave, when all runtime ids are reset + // TODO: there might be some better place for this, but whatever + public static int currentGlobalWorldGeneration = 0; + + /* UTILS */ + + private static BlockState makeBlockState(long actor, int x, int y, int z) { + BlockSource region = BlockSource.getDefaultForActor(actor); + if (region == null) + return new org.innercore.icstd.region.BlockState(0, 0); + return region.getBlock(x, y, z); + } + + private static BlockState makeBlockState(BlockSource region, int x, int y, int z) { + if (region == null) + return new BlockState(0, 0); + return region.getBlock(x, y, z); + } + /* FALLBACK */ public static void onCallbackExceptionOccurred(String callback, Throwable error) { @@ -176,6 +198,8 @@ public static void onLocalServerStarted() { private static long serverTickDisabledDueToErrorMillis = 0; private static int globalServerTickCounter = 0; + private static HashMap serverSideDimensionIsLoaded = new HashMap<>(); + // this is called, when ServerLevel is constructed public static void onLevelCreated() { // reset server tick @@ -237,10 +261,28 @@ private static void onLevelPostLoaded(boolean isServer) { Callback.invokeAPICallback("LevelLoaded", isServer); Callback.invokeAPICallback("ServerLevelLoaded"); + GameObjectManager.getGlobalForServer().setScopeLoaded(true); + for (Integer dimensionId : serverSideDimensionIsLoaded.keySet()) + if (serverSideDimensionIsLoaded.get(dimensionId)) + GameObjectManager.getGlobalForServer().setScopeLoaded(true); + // setup server tick priority setupThreadPriorityFromConfig(); } + public static void onDimensionLoaded(int id) { + Callback.invokeAPICallback("DimensionLoadedServer", id); + serverSideDimensionIsLoaded.put(id, true); + + GameObjectManager.getForDimension(id).setScopeLoaded(true); + } + + public static void onDimensionUnloaded(int id) { + Callback.invokeAPICallback("DimensionUnloadedServer", id); + GameObjectManager.getForDimension(id).setScopeLoaded(false); + serverSideDimensionIsLoaded.put(id, false); + } + public static void onGameStopped(boolean isServer) { if (!isServer) { // reset local tick @@ -270,6 +312,11 @@ public static void onGameStopped(boolean isServer) { // new pre-left callback Callback.invokeAPICallback("ServerLevelPreLeft"); + GameObjectManager.getGlobalForServer().setScopeLoaded(false); + for (GameObjectManager mgr : GameObjectManager.getForAllExistingDimensions().values()) + mgr.setScopeLoaded(false); + GameObjectManager.getGlobalForServer().setScopeLoaded(false); + // reset networking Network.getSingleton().shutdownServer(); NativeNetworking.onLevelLeft(isServer); @@ -279,6 +326,8 @@ public static void onGameStopped(boolean isServer) { // on server side save all data WorldDataSaverHandler.getInstance().onLevelLeft(); + GameObjectManager.clearAllDefaultServerScenes(false); + // legacy callback for server left if (callLegacyCallback) { Callback.invokeAPICallback("LevelLeft", isServer); @@ -303,7 +352,7 @@ public static void onGameStopped(boolean isServer) { NativePathNavigation.cleanup(); // block source by dimension cache for server ticking thread - NativeBlockSource.resetDefaultBlockSources(); + BlockSource.resetDefaultBlockSources(); // id conversion map - it will be cleared on next level load or connection // anyway, so clear it only on server side to not fuck up last server tick @@ -348,18 +397,15 @@ public static void onTick() { if (!isServerTickDisabledDueToError) { try { // tick internal systems + InventorySource.tick(); + ArmorRegistry.onTick(); WorldDataSaverHandler.getInstance().onTick(); // run callback and updatable TickExecutor executor = TickExecutor.getInstance(); - if (executor.isAvailable()) { - executor.execute(Callback.getCallbackAsRunnableList("tick", EMPTY_ARGS)); - } else { - Callback.invokeAPICallbackUnsafe("tick", EMPTY_ARGS); - } - Updatable.getForServer().onTick(); + Events.invokeScriptEvent(null, "tick", EMPTY_ARGS); + Updatable.getForServer().onTick(getGlobalServerTickCounter()); executor.blockUntilExecuted(); - ConnectedClientList.onTick(); // run ecs serverTickingSystem.run(ECS.getEntityManager()); @@ -387,6 +433,8 @@ public static void onTick() { // call server player handlers NetworkPlayerRegistry.getSingleton().onTick(); + GameObjectManager.runTickForDefaultsOnServer(); + // execute all stuff posted on server thread MainThreadQueue.serverThread.executeQueue(); @@ -404,7 +452,7 @@ public static void onBlockDestroyed(int x, int y, int z, int side, long player) isDestroyBlockCallbackInProgress = true; // check, if we can get block source - NativeBlockSource blockSource = NativeBlockSource.getDefaultForActor(player); + BlockSource blockSource = BlockSource.getDefaultForActor(player); if (blockSource == null) { return; } @@ -421,16 +469,17 @@ public static void onBlockDestroyed(int x, int y, int z, int side, long player) BlockState block = blockSource.getBlock(x, y, z); Callback.invokeAPICallback("BreakBlock", blockSource, - new Coords(x, y, z, side), + GenericScriptPos3.makeWithSideAndRelative(x, y, z, side).toScriptableObject(), block, EngineConfig.isDeveloperMode() || new NativePlayer(player).getGameMode() != 1, player, - new ItemInstance(EntityMethod.getEntityCarriedItem(player))); + ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(player)) + ); // DestroyBlock callback is called, when player breaks the block, this is // considered legacy callback if (!NativeAPI.isDefaultPrevented()) { - Callback.invokeAPICallback("DestroyBlock", new Coords(x, y, z, side), block, player); + Callback.invokeAPICallback("DestroyBlock", GenericScriptPos3.makeWithSideAndRelative(x, y, z, side).toScriptableObject(), block, player); } if (!NativeAPI.isDefaultPrevented()) { @@ -451,13 +500,11 @@ public static void onBlockDestroyed(int x, int y, int z, int side, long player) } public static void onBlockBuild(int x, int y, int z, int side, long player) { - Callback.invokeAPICallback("BuildBlock", new Coords(x, y, z, side), new FullBlock(player, x, y, z), player); + Callback.invokeAPICallback("BuildBlock", GenericScriptPos3.makeWithSideAndRelative(x, y, z, side).toScriptableObject(), makeBlockState(player, x, y, z), player); } - public static void onBlockChanged(int x, int y, int z, int id1, int data1, int id2, int data2, int dimension, int i2, - Level level) { - Callback.invokeAPICallback("BlockChanged", new Coords(x, y, z), new FullBlock(id1, data1), - new FullBlock(id2, data2), dimension, i2, NativeBlockSource.getFromServerCallbackPointer(level)); + public static void onBlockChanged(int x, int y, int z, int id1, int data1, int id2, int data2, int i1, int i2, Level region) { + Callback.invokeAPICallback("BlockChanged", new GenericScriptPos3(x, y, z).toScriptableObject(), new BlockState(id1, data1), new BlockState(id2, data2), i1, i2, BlockSource.getFromServerCallbackPointer(region)); } public static void onItemUsed(int x, int y, int z, int side, float fx, float fy, float fz, boolean isServer, @@ -470,30 +517,33 @@ public static void onItemUsed(int x, int y, int z, int side, float fx, float fy, } // initialize coordinates - Coords coords = new Coords(x, y, z, side); - coords.put("vec", coords, new Coords(fx, fy, fz)); + GenericScriptPos3 coords = GenericScriptPos3.makeWithSideAndRelative(x, y, z, side); + coords.vec = new GenericScriptPos3(fx, fy, fz); + Object coordsObj = coords.toScriptableObject(); // call server callback - ItemInstance item = new ItemInstance(EntityMethod.getEntityCarriedItem(player)); - FullBlock block = new FullBlock(player, x, y, z); - Callback.invokeAPICallback("ItemUse", coords, item, block, isExternal, player); - Callback.invokeAPICallback("ItemUseServer", coords, item, block, player); + ItemInstance item = ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(player)); + BlockState block = makeBlockState(player, x, y, z); + + Callback.invokeAPICallback("ItemUse", coordsObj, item, block, isExternal, player); + Callback.invokeAPICallback("ItemUseServer", coordsObj, item, block, player); } - public static void onExplode(float x, float y, float z, float power, long entity, boolean b1, boolean b2, - float anotherFloat) { - Callback.invokeAPICallback("Explosion", new Coords(x, y, z), + public static void onExplode(float x, float y, float z, float power, long entity, boolean b1, boolean b2, float anotherFloat) { + Callback.invokeAPICallback("Explosion", new GenericScriptPos3(x, y, z), new ScriptableParams( new Pair("power", power), new Pair("entity", entity), new Pair("onFire", b1), new Pair("someBool", b2), - new Pair("someFloat", anotherFloat))); + new Pair("someFloat", anotherFloat) + ) + ); } @ZoteOnly public static void onServerCommand(String command, float x, float y, float z, long entity, boolean fromPacket) { - Callback.invokeAPICallback("ServerCommand", command, new Coords(x, y, z), entity, fromPacket); + Callback.invokeAPICallback("ServerCommand", command, new GenericScriptPos3(x, y, z), entity, fromPacket); } /* PLAYER CALLBACKS */ @@ -529,7 +579,7 @@ public static void onEntityAttacked(long entity, long attacker) { } public static void onInteractWithEntity(long entity, long player, float x, float y, float z) { - Callback.invokeAPICallback("EntityInteract", entity, player, new Coords(x, y, z)); + Callback.invokeAPICallback("EntityInteract", entity, player, new GenericScriptPos3(x, y, z)); } // handling legacy block breaking logic, that manually drops items and @@ -596,13 +646,12 @@ public static void onEntityRemoved(long entity) { } public static void onEntityPickUpDrop(long entity, long dropEntity, int count) { - ItemInstance dropStack = new ItemInstance(new NativeItemInstance(EntityMethod.getItemFromDrop(dropEntity))); + ItemInstance dropStack = ItemInstance.makeFrom(EntityMethod.getItemFromDrop(dropEntity)); Callback.invokeAPICallback("EntityPickUpDrop", entity, dropEntity, dropStack, count); } - public static void onExpOrbsSpawned(Level level, int amount, float x, float y, float z, long player) { - Callback.invokeAPICallback("ExpOrbsSpawned", NativeBlockSource.getFromServerCallbackPointer(level), amount, - new Coords(x, y, z), player); + public static void onExpOrbsSpawned(Level region, int amount, float x, float y, float z, long player) { + Callback.invokeAPICallback("ExpOrbsSpawned", BlockSource.getFromServerCallbackPointer(region), amount, new GenericScriptPos3(x, y, z), player); } public static void onEntityDied(long entity, long attacker, int damageType) { @@ -621,19 +670,14 @@ public static void onEntityHurt(long entity, long attacker, int damageType, int // } } - public static void onThrowableHit(long projectile, float hitX, float hitY, float hitZ, long entity, int blockX, - int blockY, int blockZ, int blockSide, int itemId, int itemCount, int itemData, - NativeItemInstanceExtra itemExtra) { - Callback.invokeAPICallback("ProjectileHit", projectile, - new ItemInstance(itemId, itemCount, itemData, itemExtra), - new ScriptableParams( - new Pair("x", hitX), - new Pair("y", hitY), - new Pair("z", hitZ), - new Pair("entity", entity), - new Pair("coords", - blockX == 0 && blockY == 0 && blockZ == 0 && blockSide == 0 ? null - : new Coords(blockX, blockY, blockZ, blockSide)))); + public static void onThrowableHit(long projectile, float hitX, float hitY, float hitZ, long entity, int blockX, int blockY, int blockZ, int blockSide, int itemId, int itemCount, int itemData, ItemInstanceExtra itemExtra) { + Callback.invokeAPICallback("ProjectileHit", projectile, new ItemInstance(itemId, itemCount, itemData, itemExtra), new ScriptableParams( + new Pair("x", hitX), + new Pair("y", hitY), + new Pair("z", hitZ), + new Pair("entity", entity), + new Pair("coords", blockX == 0 && blockY == 0 && blockZ == 0 && blockSide == 0 ? null : GenericScriptPos3.makeWithSideAndRelative(blockX, blockY, blockZ, blockSide)) + )); } public static void onPathNavigationDone(long entity, int result) { @@ -643,15 +687,16 @@ public static void onPathNavigationDone(long entity, int result) { /* BLOCK CALLBACKS */ public static void onRedstoneSignalChange(int x, int y, int z, int signal, boolean isLoadingChange, Level level) { - NativeBlockSource blockSource = NativeBlockSource.getFromServerCallbackPointer(level); - Callback.invokeAPICallback("RedstoneSignal", new Coords(x, y, z), new ScriptableParams( + BlockSource blockSource = BlockSource.getFromServerCallbackPointer(level); + Callback.invokeAPICallback("RedstoneSignal", new GenericScriptPos3(x, y, z), new ScriptableParams( new Pair("power", signal), new Pair("signal", signal), - new Pair("onLoad", isLoadingChange)), new FullBlock(blockSource, x, y, z), blockSource); + new Pair("onLoad", isLoadingChange) + ), makeBlockState(blockSource, x, y, z), blockSource); } public static void onRandomBlockTick(int x, int y, int z, int id, int data, Level level) { - NativeBlock.onRandomTickCallback(x, y, z, id, data, NativeBlockSource.getFromServerCallbackPointer(level)); + NativeBlock.onRandomTickCallback(x, y, z, id, data, BlockSource.getFromServerCallbackPointer(level)); } @Deprecated(since = "Zote") @@ -660,46 +705,41 @@ public static void onAnimateBlockTick(int x, int y, int z, int id, int data) { } public static void onBlockSpawnResources(int x, int y, int z, int id, int data, float f, int i, Level level) { - Callback.invokeAPICallback("PopBlockResources", new Coords(x, y, z), new FullBlock(id, data), (double) f, i, - NativeBlockSource.getFromServerCallbackPointer(level)); + Callback.invokeAPICallback("PopBlockResources", new GenericScriptPos3(x, y, z), new BlockState(id, data), (double) f, i, BlockSource.getFromServerCallbackPointer(level)); } public static void onBlockEventEntityInside(int x, int y, int z, long entity) { - Callback.invokeAPICallback("BlockEventEntityInside", new Coords(x, y, z), new FullBlock(entity, x, y, z), - entity); + Callback.invokeAPICallback("BlockEventEntityInside", new GenericScriptPos3(x, y, z), makeBlockState(entity, x, y, z), entity); } public static void onBlockEventEntityStepOn(int x, int y, int z, long entity) { - Callback.invokeAPICallback("BlockEventEntityStepOn", new Coords(x, y, z), new FullBlock(entity, x, y, z), - entity); + Callback.invokeAPICallback("BlockEventEntityStepOn", new GenericScriptPos3(x, y, z), makeBlockState(entity, x, y, z), entity); } public static void onBlockEventNeighbourChange(int x, int y, int z, int changedX, int changedY, int changedZ, Level level) { - NativeBlockSource blockSource = NativeBlockSource.getFromServerCallbackPointer(level); - Callback.invokeAPICallback("BlockEventNeighbourChange", new Coords(x, y, z), - new FullBlock(blockSource, x, y, z), new Coords(changedX, changedY, changedZ), blockSource); + BlockSource blockSource = BlockSource.getFromServerCallbackPointer(level); + Callback.invokeAPICallback("BlockEventNeighbourChange", new GenericScriptPos3(x, y, z), makeBlockState(blockSource, x, y, z), new GenericScriptPos3(changedX, changedY, changedZ), blockSource); } /* ITEM CALLBACKS */ public static void onItemUsedNoTarget(long player) { - Callback.invokeAPICallback("ItemUseNoTarget", new ItemInstance(EntityMethod.getEntityCarriedItem(player)), - player); + Callback.invokeAPICallback("ItemUseNoTarget", ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(player)), player); } public static void onItemUseReleased(int ticks, long player) { - Callback.invokeAPICallback("ItemUsingReleased", new ItemInstance(EntityMethod.getEntityCarriedItem(player)), + Callback.invokeAPICallback("ItemUsingReleased", ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(player)), ticks, player); } public static void onItemUseComplete(long player) { - Callback.invokeAPICallback("ItemUsingComplete", new ItemInstance(EntityMethod.getEntityCarriedItem(player)), + Callback.invokeAPICallback("ItemUsingComplete", ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(player)), player); } public static void onItemDispensed(float x, float y, float z, int side, int id, int count, int data, - NativeItemInstanceExtra extra, Level level, int slot) { + ItemInstanceExtra extra, Level level, int slot) { int rx = (int) Math.floor(x); int ry = (int) Math.floor(y); int rz = (int) Math.floor(z); @@ -724,19 +764,16 @@ public static void onItemDispensed(float x, float y, float z, int side, int id, ix--; break; } - Coords coords = new Coords(ix, iy, iz); - coords.put("relative", coords, new Coords(rx, ry, rz)); - coords.put("vec", coords, new Coords(x, y, z)); - coords.setSide(side); - Callback.invokeAPICallback("ItemDispensed", coords, - new ItemInstance(id, count, data, extra), - NativeBlockSource.getFromServerCallbackPointer(level), slot); + GenericScriptPos3 coords = new GenericScriptPos3(ix, iy, iz, side); + coords.relative = new GenericScriptPos3(rx, ry, rz); + coords.vec = new GenericScriptPos3(x, y, z); + Callback.invokeAPICallback("ItemDispensed", coords, new ItemInstance(id, count, data, extra), BlockSource.getFromServerCallbackPointer(level), slot); } /* ENCHANT CALLBACKS */ public static void onEnchantPostAttack(int enchantId, int damage, long actor1, long actor2) { - ItemStack item = new ItemStack(); + ItemInstance item = ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(actor1)); Callback.invokeAPICallback("EnchantPostAttack", enchantId, item, damage, actor1, actor2); if (!NativeAPI.isDefaultPrevented()) { NativeCustomEnchant enchant = NativeCustomEnchant.getEnchantById(enchantId); @@ -748,8 +785,8 @@ public static void onEnchantPostAttack(int enchantId, int damage, long actor1, l } public static void onEnchantPostHurt(int enchantId, int itemId, int itemCount, int itemData, - NativeItemInstanceExtra itemExtra, int damage, long actor1, long actor2) { - ItemStack item = new ItemStack(itemId, itemCount, itemData, itemExtra); + ItemInstanceExtra itemExtra, int damage, long actor1, long actor2) { + ItemInstance item = new ItemInstance(itemId, itemCount, itemData, itemExtra); Callback.invokeAPICallback("EnchantPostHurt", enchantId, item, damage, actor1, actor2); if (!NativeAPI.isDefaultPrevented()) { NativeCustomEnchant enchant = NativeCustomEnchant.getEnchantById(enchantId); @@ -803,9 +840,8 @@ public static void onWorkbenchCraft(CraftingGrid grid, long player, int size) { // handle modded recipe: replace vanilla craft logic with modded one NativeAPI.preventDefault(); ItemInstance result = recipe.provideRecipeForPlayer(container, player); - if (result != null && result.getId() != 0 && result.getCount() > 0) { - new NativePlayer(player).addItemToInventory(result.getId(), result.getCount(), result.getData(), - result.getExtra(), true); + if (result != null && result.id != 0 && result.count > 0) { + new NativePlayer(player).addItemToInventory(result.id, result.count, result.data, result.extra, true); Callback.invokeCallback("VanillaWorkbenchPostCraft", result, container, player, recipe); } container.apply(); @@ -853,6 +889,7 @@ public static void onLocalChunkLoaded(int dimension, int chunkX, int chunkZ) { public static void onChunkLoaded(int dimension, int chunkX, int chunkZ) { Callback.invokeAPICallback("ChunkLoaded", dimension, chunkX, chunkZ); + GameObjectManager.getForDimension(dimension).markChunkForUpdate(chunkX, chunkZ); } @ClientOnly @@ -862,6 +899,7 @@ public static void onLocalChunkDiscarded(int dimension, int chunkX, int chunkZ) public static void onChunkDiscarded(int dimension, int chunkX, int chunkZ) { Callback.invokeAPICallback("ChunkDiscarded", dimension, chunkX, chunkZ); + GameObjectManager.getForDimension(dimension).markChunkForUpdate(chunkX, chunkZ); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeCustomEnchant.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeCustomEnchant.java index dab51bdac..53ca8f8ed 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeCustomEnchant.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeCustomEnchant.java @@ -1,11 +1,11 @@ package com.zhekasmirnov.innercore.api; import com.reider745.item.CustomEnchantment; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; import com.zhekasmirnov.apparatus.mod.ContentIdSource; import cn.nukkit.item.enchantment.Enchantment; +import org.innercore.icstd.inventory.ItemInstance; import org.mozilla.javascript.annotations.JSStaticFunction; import java.util.HashMap; @@ -142,7 +142,7 @@ public NativeCustomEnchant setAttackDamageBonusProvider(AttackDamageBonusProvide } public interface DoPostAttackListener { - void doPostAttack(ItemStack item, int damage, long actor1, long actor2); + void doPostAttack(ItemInstance item, int damage, long actor1, long actor2); } private DoPostAttackListener doPostAttackListener; @@ -174,7 +174,7 @@ public NativeCustomEnchant setProtectionBonusProvider(ProtectionBonusProvider pr } public interface DoPostHurtListener { - void doPostHurt(ItemStack item, int damage, long actor1, long actor2); + void doPostHurt(ItemInstance item, int damage, long actor1, long actor2); } private DoPostHurtListener doPostHurtListener; diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeGameController.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeGameController.java index 3b50fe851..bdca1da83 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeGameController.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeGameController.java @@ -3,7 +3,6 @@ import com.reider745.InnerCoreServer; import com.reider745.api.ClientOnly; import org.mozilla.javascript.annotations.JSStaticFunction; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; @Deprecated @ClientOnly diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeItem.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeItem.java index 2fb068a88..03f5a7193 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeItem.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeItem.java @@ -1,405 +1,233 @@ package com.zhekasmirnov.innercore.api; import com.reider745.api.CustomManager; -import com.reider745.api.ZoteOnly; -import com.reider745.item.CustomItem; -import com.reider745.item.ItemMethod; -import com.reider745.item.ItemMethod.PropertiesNames; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.innercore.icstd.modules.ItemModule; +import org.mozilla.javascript.annotations.JSStaticFunction; + import com.zhekasmirnov.apparatus.ecs.ECS; import com.zhekasmirnov.apparatus.ecs.core.ComponentCollection; import com.zhekasmirnov.apparatus.ecs.core.EntityManager; import com.zhekasmirnov.apparatus.ecs.types.ECSTags; import com.zhekasmirnov.apparatus.ecs.types.item.ArmorItemComponent; import com.zhekasmirnov.apparatus.ecs.types.item.ItemComponent; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; -import com.zhekasmirnov.innercore.api.runtime.Callback; +import com.zhekasmirnov.innercore.api.mod.adaptedscript.AdaptedScriptAPI; +import com.zhekasmirnov.innercore.api.runtime.other.ArmorRegistry; import com.zhekasmirnov.innercore.api.runtime.other.NameTranslation; -import com.zhekasmirnov.innercore.mod.resource.ResourcePackManager; - -import cn.nukkit.item.Item; -import org.mozilla.javascript.NativeArray; -import org.mozilla.javascript.annotations.JSStaticFunction; - -import java.util.HashSet; /** * Created by zheka on 26.07.2017. */ - +@Deprecated public class NativeItem { public static final int MAX_ITEM_ID = 32768; private static NativeItem[] itemById = new NativeItem[MAX_ITEM_ID]; public int id; - private CustomManager properties; public final String nameId; public final String nameToDisplay; public final int entity; - private static final ComponentCollection initCC = new ComponentCollection() - .setTypes(ItemComponent.COMPONENT_ID, ECSTags.CONTENT_ID); - - @Deprecated(since = "Zote") - protected NativeItem(int id, long ptr, String nameId, String nameToDisplay) { - throw new UnsupportedOperationException("NativeItem(id, ptr, nameId, nameToDisplay)"); - } - - protected NativeItem(int id, CustomManager itemManager, String nameId, String nameToDisplay) { + protected NativeItem(int id, String nameId, String nameToDisplay) { this.id = id; - this.properties = itemManager; this.nameId = nameId; this.nameToDisplay = nameToDisplay; itemById[id] = this; + NameTranslation.sendNameToGenerateCache(id, 0, nameToDisplay); EntityManager em = ECS.getEntityManager(); - this.entity = em.createEntity(); + entity = em.createEntity(); em.extend(entity, initCC.setValues(new ItemComponent(id, nameId, nameToDisplay))); } - public static NativeItem newNativeItem(int id, CustomManager manager, String nameId, String name) { - return new NativeItem(id, manager, nameId, name); + private static final ComponentCollection initCC = new ComponentCollection() + .setTypes(ItemComponent.COMPONENT_ID, ECSTags.CONTENT_ID); + + public static NativeItem constructLegacyItemComponent(int id, String nameId, String name) { + return new NativeItem(id, nameId, name); + } + + public static NativeItem constructBlock(int id, CustomManager manager, String nameId, String name) { + return new NativeItem(id, nameId, name); + } + + + private static final ComponentCollection armorCC = new ComponentCollection().setTypes(ArmorItemComponent.COMPONENT_ID); + + public static NativeItem constructLegacyArmorComponent(int id, String nameId, String name, int slot, int defense, float knockbackResist) { + NativeItem item = new NativeItem(id, nameId, name); + ArmorItemComponent component = new ArmorItemComponent(slot, defense, knockbackResist); + ArmorRegistry.registerArmor(id, component); + ECS.getEntityManager().extend(item.entity, armorCC.setValues(component)); + return item; + } + + private static final ComponentCollection throwableCC = new ComponentCollection().setTypes("tag:throwable-item"); + + public static NativeItem constructLegacyThrowableComponent(int id, String nameId, String name) { + NativeItem item = new NativeItem(id, nameId, name); + ECS.getEntityManager().extend(item.entity, throwableCC); + return item; + } + + // backwards compatibility methods + public static NativeItem getItemById(int id) { + if (id < 0 || id >= MAX_ITEM_ID) { + return null; + } + AdaptedScriptAPI.logDeprecation("NativeItem.getItemById"); + return itemById[id]; } - @Deprecated(since = "Zote") public void setGlint(boolean val) { - setGlint(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setGlint"); + ItemModule.setGlint(id, val); } public void setHandEquipped(boolean val) { - setHandEquipped(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setHandEquipped"); + ItemModule.setHandEquipped(id, val); } public void setLiquidClip(boolean val) { - setLiquidClip(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setLiquidClip"); + ItemModule.setLiquidClip(id, val); } - @Deprecated(since = "Zote") public void setUseAnimation(int val) { - setUseAnimation(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setUseAnimation"); + ItemModule.setUseAnimation(id, val); } public void setMaxUseDuration(int val) { - setMaxUseDuration(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setMaxUseDuration"); + ItemModule.setMaxUseDuration(id, val); } public void setMaxDamage(int val) { - setMaxDamage(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setMaxDamage"); + ItemModule.setMaxDamage(id, val); } public void setMaxStackSize(int val) { - setMaxStackSize(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setMaxStackSize"); + ItemModule.setMaxStack(id, val); } public void setStackedByData(boolean val) { - setStackedByData(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setStackedByData"); + ItemModule.setStackedByData(id, val); } - @Deprecated(since = "Zote") public void setAllowedInOffhand(boolean val) { - setAllowedInOffhand(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setAllowedInOffhand"); + ItemModule.setAllowedInOffhand(id, val); } public void setCreativeCategory(int val) { - setCreativeCategory(id, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setCreativeCategory"); + ItemModule.setCreativeCategory(id, val); } - @Deprecated(since = "Zote") public void setProperties(String val) { - setProperties(properties, val); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setProperties"); + ItemModule.setProperties(id, val); } public void setEnchantType(int type, int value) { - setEnchantability(type, value); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setEnchantType"); + ItemModule.setEnchantType(id, type, value); } - // TODO: Nukkit-MOT's getEnchantAbility not used anywhere. public void setEnchantability(int type, int value) { - setEnchantability(properties, type, value); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setEnchantability"); + ItemModule.setEnchantType(id, type, value); } public void setEnchantType(int type) { + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setEnchantType"); setEnchantType(type, 1); } - public void addRepairItem(int id) { + public void addRepairItem(int repairId) { + AdaptedScriptAPI.logIndevDeprecation("NativeItem.addRepairItem"); + ItemModule.addRepairItem(id, repairId); } - public void addRepairItems(int... ids) { - for (int id : ids) { - addRepairItem(id); + public void addRepairItems(int... repairIds) { + AdaptedScriptAPI.logIndevDeprecation("NativeItem.addRepairItems"); + for (int repairId : repairIds) { + ItemModule.addRepairItem(id, repairId); } } public void setArmorDamageable(boolean value) { - setArmorDamageable(properties, value); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setArmorDamageable"); + ItemModule.setArmorDamageable(id, value); } public void setShouldDespawn(boolean value) { - ItemMethod.setShouldDespawn(id, value); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setShouldDespawn"); + ItemModule.setShouldDespawn(id, value); } public void setFireResistant(boolean value) { - ItemMethod.setFireResistant(properties, value); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setFireResistant"); + ItemModule.setFireResistant(id, value); } public void setExplodable(boolean value) { - ItemMethod.setExplodable(id, value); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setExplodable"); + ItemModule.setExplodable(id, value); } - @JSStaticFunction - public static NativeItem getItemById(int id) { - if (id < 0 || id >= MAX_ITEM_ID) { - return null; - } - return itemById[id]; - } - @JSStaticFunction public static NativeItem createItem(int id, String nameId, String name, String iconName, int iconIndex) { - nameId = NativeAPI.convertNameId(nameId); // any name id must be lowercase - return new NativeItem(id, constructItem(id, nameId, name, iconName, iconIndex), nameId, name); + AdaptedScriptAPI.logDeprecation("NativeItem.createItem"); + return ItemModule.createItem(id, nameId, name, iconName, iconIndex); } @JSStaticFunction - public static NativeItem createFoodItem(int id, String nameId, String name, String iconName, int iconIndex, - int food) { - nameId = NativeAPI.convertNameId(nameId); - return new NativeItem(id, constructFoodItem(id, nameId, name, iconName, iconIndex, food), nameId, name); + public static NativeItem createArmorItem(int id, String nameId, String name, String iconName, int iconIndex, String texture, int slot, int defense, int durability, double knockbackResist) { + AdaptedScriptAPI.logDeprecation("NativeItem.createArmorItem"); + return ItemModule.createArmorItem(id, nameId, name, iconName, iconIndex, texture, slot, defense, durability, (float) knockbackResist); } - private static final ComponentCollection armorCC = new ComponentCollection() - .setTypes(ArmorItemComponent.COMPONENT_ID); - - @JSStaticFunction - public static NativeItem createArmorItem(int id, String nameId, String name, String iconName, int iconIndex, - String texture, int slot, int defense, int durability, double knockbackResist) { - nameId = NativeAPI.convertNameId(nameId); // any name id must be lowercase - ArmorItemComponent component = new ArmorItemComponent(slot, defense, (float) knockbackResist); - NativeItem item = new NativeItem(id, constructArmorItem(id, nameId, name, iconName, iconIndex, texture, slot, - defense, durability, (float) knockbackResist), nameId, name); - // TODO: ArmorRegistry.registerArmor(id, component); - ECS.getEntityManager().extend(item.entity, armorCC.setValues(component)); - return item; - } - - private static final ComponentCollection throwableCC = new ComponentCollection().setTypes("tag:throwable-item"); - @JSStaticFunction public static NativeItem createThrowableItem(int id, String nameId, String name, String iconName, int iconIndex) { - nameId = NativeAPI.convertNameId(nameId); // any name id must be lowercase - NativeItem item = new NativeItem(id, constructThrowableItem(id, nameId, name, iconName, iconIndex), nameId, - name); - ECS.getEntityManager().extend(item.entity, throwableCC); - return item; - } - - @JSStaticFunction - public static boolean isGlintItemInstance(int id, int data, Object extra) { - return NativeAPI.isGlintItemInstance(id, data, NativeItemInstanceExtra.unwrapObject(extra)); - } - - /* - * native part - */ - - public static CustomManager constructItem(int id, String nameId, String name, String iconName, int iconIndex) { - return CustomItem.registerItem(nameId, id, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name)); - } - - public static CustomManager constructFoodItem(int id, String nameId, String name, String iconName, int iconIndex, - int food) { - return CustomItem.registerItemFood(nameId, id, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name), - food); - } - - public static CustomManager constructArmorItem(int id, String nameId, String name, String iconName, int iconIndex, - String texture, int slot, int defense, int durability, float knockbackResist) { - return CustomItem.registerArmorItem(nameId, id, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name), - slot, defense, durability, (float) knockbackResist); - } - - public static CustomManager constructThrowableItem(int id, String nameId, String name, String iconName, - int iconIndex) { - return CustomItem.registerThrowableItem(nameId, id, - NameTranslation.fixUnicodeIfRequired("item_" + nameId, name)); - } - - @Deprecated(since = "Zote") - public static void setGlint(CustomManager manager, boolean val) { + AdaptedScriptAPI.logDeprecation("NativeItem.createThrowableItem"); + return ItemModule.createThrowableItem(id, nameId, name, iconName, iconIndex); } - public static void setHandEquipped(CustomManager manager, boolean val) { - ItemMethod.setHandEquipped(manager, val); - } - - public static void setLiquidClip(CustomManager manager, boolean val) { - ItemMethod.setLiquidClip(manager, val); - } - - @Deprecated(since = "Zote") - public static void setUseAnimation(CustomManager manager, int val) { - } - - public static void setMaxUseDuration(CustomManager manager, int val) { - ItemMethod.setMaxUseDuration(manager, val); - } - - public static void setMaxDamage(CustomManager manager, int val) { - ItemMethod.setMaxDamage(manager, val); - } - - public static void setMaxStackSize(CustomManager manager, int val) { - ItemMethod.setMaxStackSize(manager, val); - } - - public static void setStackedByData(CustomManager manager, boolean val) { - ItemMethod.setStackedByData(manager, val); - } - - @Deprecated(since = "Zote") - public static void setAllowedInOffhand(CustomManager manager, boolean val) { - // JIC if something changed, check PlayerOffhandInventory.OFFHAND_ITEMS - } - - public static void setCreativeCategory(int id, int val) { - ItemMethod.setCreativeCategory(id, val); - } - - @Deprecated(since = "Zote") - public static void setProperties(CustomManager manager, String val) { - } - - public static void setEnchantability(CustomManager manager, int type, int value) { - ItemMethod.setEnchantability(manager, type, value); - } - - public static void setArmorDamageable(CustomManager manager, boolean value) { - ItemMethod.setArmorDamageable(manager, value); - } - - public static void addRepairItemId(CustomManager manager, int id) { - ItemMethod.addRepairItemId(manager, id); - } - - public static int getMaxStackForId(int id, int data) { - return ItemMethod.getMaxStackForId(id, data); - } - - public static int getMaxDamageForId(int id, int data) { - return ItemMethod.getMaxDamageForId(id, data); - } - - public static String getNameForId(int id, int data, Item extra) { - return ItemMethod.getNameForId(id, data, NativeItemInstanceExtra.getExtraOrNull(extra)); - } - - public static String getNameForId(int id, int data, NativeItemInstanceExtra extra) { - return ItemMethod.getNameForId(id, data, extra); - } @JSStaticFunction - public static int getArmorValue(int id){ - return ItemMethod.getArmorValue(id); - } - - public static void setCreativeCategoryForId(int id, int category) { - ItemMethod.setCreativeCategory(id, category); - } - - public static String getNameForId(int id, int data) { - return getNameForId(id, data, (NativeItemInstanceExtra) null); - } - - @JSStaticFunction - public static boolean isValid(int id) { - return NativeAPI.getStringIdAndTypeForIntegerId(id) != null; - } - - public static void addToCreativeInternal(int id, int count, int data, Item extra) { - CustomItem.addCreative(id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); - } - - public static void addToCreativeInternal(int id, int count, int data, NativeItemInstanceExtra extra) { - CustomItem.addCreative(id, count, data, extra); - } - - @JSStaticFunction - public static void addToCreativeGroup(String groupName, String displayName, int id) { - CustomItem.addToCreativeGroup(groupName, id); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setCreativeCategoryForId"); + ItemModule.setCreativeCategory(id, category); } @JSStaticFunction public static void addToCreative(int id, int count, int data, Object extra) { - addToCreativeInternal(id, count, data, NativeItemInstanceExtra.unwrapObject(extra)); + AdaptedScriptAPI.logIndevDeprecation("NativeItem.addToCreative"); + ItemModule.addToCreative(id, count, data, extra); } @JSStaticFunction - public static void setCategoryForId(int id, int category) { - setCreativeCategoryForId(id, category); + public static String getDynamicItemIconOverride(int id, int count, int data, ItemInstanceExtra extra) { + AdaptedScriptAPI.logIndevDeprecation("NativeItem.getDynamicItemIconOverride"); + return ItemModule.getDynamicIconOverride(id, count, data, extra); } @JSStaticFunction - public static void addCreativeGroup(String groupName, String displayName, NativeArray ids) { - for (int i = 0; i < ids.getLength(); i++) { - addToCreativeGroup(groupName, displayName, ((Number) ids.get(i)).intValue()); - } - } - - public static final Object DYNAMIC_ICON_LOCK = new Object(); - public static final Object DYNAMIC_NAME_LOCK = new Object(); - - private static HashSet itemIdsWithDynamicIcon = new HashSet<>(); - - @Deprecated(since = "Zote") - public static void setItemRequiresIconOverride(int id, boolean enabled) { - if (enabled) { - itemIdsWithDynamicIcon.add(id); - } else { - itemIdsWithDynamicIcon.remove(id); - } - } - - @Deprecated(since = "Zote") - public static boolean isDynamicIconItem(int id) { - return itemIdsWithDynamicIcon.contains(id); - } - - private static boolean isInnerCoreUIOverride = false; - private static String lastIconOverridePath = null; - - @Deprecated(since = "Zote") public static void overrideItemIcon(String name, int index) { - synchronized (DYNAMIC_ICON_LOCK) { - if (!isInnerCoreUIOverride) { - NativeAPI.overrideItemIcon(name, index); - } - lastIconOverridePath = ResourcePackManager.getItemTextureName(name, index); - if (lastIconOverridePath != null && !lastIconOverridePath.endsWith(".png")) { - lastIconOverridePath += ".png"; - } - } - } - - @Deprecated(since = "Zote") - public static String getLastIconOverridePath() { - return lastIconOverridePath; + AdaptedScriptAPI.logIndevDeprecation("NativeItem.overrideItemIcon"); + ItemModule.overrideCurrentIcon(name, index); } - @Deprecated(since = "Zote") - public static synchronized String getDynamicItemIconOverride(int id, int count, int data, - NativeItemInstanceExtra extra) { - if (isDynamicIconItem(id)) { - synchronized (DYNAMIC_ICON_LOCK) { - isInnerCoreUIOverride = true; - lastIconOverridePath = null; - Callback.invokeAPICallback("ItemIconOverride", new ItemInstance(id, count, data, extra), true); - isInnerCoreUIOverride = false; - return lastIconOverridePath; - } - } else { - return null; - } + @JSStaticFunction + public static void setItemRequiresIconOverride(int id, boolean enabled) { + AdaptedScriptAPI.logIndevDeprecation("NativeItem.setItemRequiresIconOverride"); + ItemModule.setRequiresIconOverride(id, enabled); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstance.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstance.java index f4f8abc7b..c50792055 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstance.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstance.java @@ -6,6 +6,7 @@ import com.reider745.hooks.item.ItemUtils; import com.reider745.item.NukkitIdConverter; import com.zhekasmirnov.horizon.runtime.logger.Logger; +import org.innercore.icstd.inventory.ItemInstanceExtra; /** * Created by zheka on 26.07.2017. @@ -14,7 +15,7 @@ public class NativeItemInstance { private Item item; public int id, count, data; - public NativeItemInstanceExtra extra; + public ItemInstanceExtra extra; public boolean isValid = false; @@ -64,7 +65,7 @@ public Item getPointer() { } CompoundTag tag = item.getOrCreateNamedTag(); if (extra != null && !extra.isEmpty()) { - tag = extra.getValue().getNamedTag(); + tag = extra.getItem().getNamedTag(); extra.bind(item); } else { tag.remove(ItemUtils.INNER_CORE_TAG_NAME); @@ -117,12 +118,12 @@ public static Item getExtra(Item item) { if (item == null) { return null; } - NativeItemInstanceExtra extra = ItemUtils.getItemInstanceExtra(item); - return extra != null ? extra.getValue() : null; + ItemInstanceExtra extra = ItemUtils.getItemInstanceExtra(item); + return extra != null ? extra.getItem() : null; } public static void setExtra(Item item, int ench) { - NativeItemInstanceExtra extra = ItemUtils.getItemInstanceExtra(item); + ItemInstanceExtra extra = ItemUtils.getItemInstanceExtra(item); if (extra != null) { extra.addEnchant(ench, 1); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstanceExtra.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstanceExtra.java index 024acabdd..dc7b7ae92 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstanceExtra.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeItemInstanceExtra.java @@ -1,605 +1,22 @@ package com.zhekasmirnov.innercore.api; -import com.reider745.hooks.item.ItemUtils; -import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import com.zhekasmirnov.innercore.api.nbt.NativeCompoundTag; -import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaver; -import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; -import com.zhekasmirnov.innercore.api.runtime.saver.serializer.ScriptableSerializer; +import org.innercore.icstd.inventory.ItemInstanceExtra; -import cn.nukkit.item.Item; -import cn.nukkit.item.enchantment.Enchantment; -import cn.nukkit.nbt.tag.CompoundTag; -import cn.nukkit.nbt.tag.ListTag; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.NativeArray; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; -import org.mozilla.javascript.Wrapper; - -/** - * Created by zheka on 14.02.2018. - */ - -public class NativeItemInstanceExtra implements ObjectSaverRegistry.IHashSaver { - private static final int saverId; - - static { - saverId = ObjectSaverRegistry.registerSaver("_item_instance_extra_data", new ObjectSaver() { - @Override - public Object read(ScriptableObject input) { - if (input == null) { - return null; - } - - NativeItemInstanceExtra extra = new NativeItemInstanceExtra(); - - NativeArray _ids = ScriptableObjectHelper.getNativeArrayProperty(input, "eId", null); - NativeArray _levels = ScriptableObjectHelper.getNativeArrayProperty(input, "eLvl", null); - if (_ids != null && _levels != null) { - Object[] ids = _ids.toArray(); - Object[] levels = _levels.toArray(); - int count = Math.min(ids.length, levels.length); - for (int i = 0; i < count; i++) { - extra.addEnchant((int) ids[i], (int) levels[i]); - } - } - - String custom = ScriptableObjectHelper.getStringProperty(input, "$", null); - if (custom != null) { - extra.setAllCustomData(custom); - } - - String name = ScriptableObjectHelper.getStringProperty(input, "N", null); - if (name != null) { - extra.setCustomName(name); - } - - return extra.isEmpty() ? null : extra; - } - - @Override - public ScriptableObject save(Object input) { - if (input != null && input instanceof NativeItemInstanceExtra) { - NativeItemInstanceExtra extra = (NativeItemInstanceExtra) input; - if (!extra.isEmpty()) { - ScriptableObject saved = ScriptableObjectHelper.createEmpty(); - - if (extra.isEnchanted()) { - int[][] enchants = extra.getRawEnchants(); - Object[] ids = new Object[enchants[0].length]; - Object[] levels = new Object[enchants[1].length]; - for (int i = 0; i < ids.length; i++) { - ids[i] = enchants[0][i]; - levels[i] = enchants[1][i]; - } - saved.put("eId", saved, ScriptableObjectHelper.createArray(ids)); - saved.put("eLvl", saved, ScriptableObjectHelper.createArray(levels)); - } - - String custom = extra.getAllCustomData(); - if (custom != null) { - saved.put("$", saved, custom); - } - - String name = extra.getCustomName(); - if (name != null) { - saved.put("N", saved, name); - } - - return saved; - } - } - return null; - } - }); - } - - public static void initSaverId() { - // forces class to load - } - - private Item item; - - public boolean isFinalizableInstance() { - return false; - } - - @Deprecated(since = "Zote") +// this is a stub class for DungeonUtility, it MUST NOT be used in code +@Deprecated +public class NativeItemInstanceExtra extends ItemInstanceExtra { public NativeItemInstanceExtra(long extra) { - throw new UnsupportedOperationException(); - } - - public NativeItemInstanceExtra(Item item) { - ObjectSaverRegistry.registerObject(this, saverId); - - if (item != null) { - this.item = item; - } else { - this.item = constructNew(); - } + super(extra); } public NativeItemInstanceExtra() { - ObjectSaverRegistry.registerObject(this, saverId); - this.item = constructNew(); - } - - public NativeItemInstanceExtra(NativeItemInstanceExtra extra) { - ObjectSaverRegistry.registerObject(this, saverId); - - Item item = getValueOrNullPtr(extra); - if (item != null) { - this.item = constructClone(item); - } else { - this.item = constructNew(); - } - } - - public JSONObject asJson() { - if (isEmpty()) { - return null; - } - JSONObject json = new JSONObject(); - try { - int[][] enchants = getRawEnchants(); - int[] ids = enchants[0]; - int[] levels = enchants[1]; - if (ids.length > 0) { - JSONArray enchantsJson = new JSONArray(); - for (int i = 0; i < ids.length; i++) { - JSONObject enchantJson = new JSONObject(); - enchantJson.put("id", ids[i]); - enchantJson.put("l", levels[i]); - enchantsJson.put(enchantJson); - } - json.put("enchants", enchantsJson); - } - String name = getCustomName(); - if (name != null && name.length() > 0) { - json.put("name", name); - } - String data = getAllCustomData(); - if (data != null && data.length() > 0) { - json.put("data", data); - } - } catch (Exception ignore) { - } - return json.length() > 0 ? json : null; - } - - public NativeItemInstanceExtra copy() { - return new NativeItemInstanceExtra(this); - } - - public Item getValue() { - return item; - } - - public void bind(Item item) { - this.item = item; - this.customData = null; - this.customDataLoaded = false; - } - - public boolean isEmpty() { - return item != null ? !item.hasCompoundTag() : true; - } - - public void applyTo(Scriptable item) { - if (item != null) { - item.put("extra", item, getValue()); - } - } - - public boolean isEnchanted() { - return item != null ? item.hasEnchantments() : false; - } - - public void addEnchant(int type, int level) { - Enchantment enchantment = Enchantment.get(type); - if (enchantment.getName().equals("%enchantment.unknown")) { - Logger.error("NativeItemInstanceExtra", "Unknown enchantment with id " + type); - return; - } - if (item != null) { - enchantment = Enchantment.getEnchantment(type); - enchantment.setLevel(level, false); - item.addEnchantment(enchantment); - } - } - - public int getEnchantLevel(int type) { - return item != null ? item.getEnchantmentLevel(type) : 0; - } - - public void removeEnchant(int type) { - if (item != null && item.hasEnchantments()) { - ListTag ench = item.getNamedTag().getList("ench", CompoundTag.class); - for (int i = 0, l = ench.size(); i < l; i++) { - if (ench.get(i).getShort("id") == type) { - ench.remove(i); - return; - } - } - } - } - - public void removeAllEnchants() { - if (item != null && item.hasEnchantments()) { - item.getNamedTag().remove("ench"); - } - } - - public int getEnchantCount() { - if (item != null && item.hasEnchantments()) { - Enchantment[] enchantments = item.getEnchantments(); - return enchantments.length; - } - return 0; - } - - public String getEnchantName(int id, int lvl) { - Enchantment enchantment = item != null ? item.getEnchantment(id) : null; - if (enchantment == null) { - enchantment = Enchantment.get(id); - } - if (!enchantment.getName().equals("%enchantment.unknown")) { - return lvl > 0 ? enchantment.getName() + " " + Enchantment.getLevelString(lvl) : enchantment.getName(); - } - return null; - } - - public String getEnchantName(int id) { - return getEnchantName(id, getEnchantLevel(id)); - } - - public int[][] getRawEnchants() { - if (item == null) { - return new int[0][0]; - } - int count = getEnchantCount(); - int ids[] = new int[count]; - int levels[] = new int[count]; - Enchantment[] enchantments = item.getEnchantments(); - for (int i = 0, l = enchantments.length; i < l; i++) { - Enchantment enchantment = enchantments[i]; - ids[i] = enchantment.getId(); - levels[i] = enchantment.getLevel(); - } - return new int[][] { - ids, - levels - }; - } - - public ScriptableObject getEnchants() { - int[][] enchants = getRawEnchants(); - int[] ids = enchants[0]; - int[] levels = enchants[1]; - - ScriptableObject result = ScriptableObjectHelper.createEmpty(); - for (int i = 0; i < ids.length; i++) { - if (levels[i] > 0) { - result.put(ids[i], result, levels[i]); - } - } - return result; - } - - public String getAllEnchantNames() { - StringBuilder str = new StringBuilder(); - - int[][] enchants = getRawEnchants(); - int[] ids = enchants[0]; - int[] levels = enchants[1]; - for (int i = 0; i < ids.length; i++) { - if (levels[i] > 0) { - str.append(getEnchantName(ids[i], levels[i])).append("\n"); - } - } - - return str.toString(); - } - - public String getAllCustomData() { - if (item != null) { - CompoundTag tag = item.getNamedTag(); - return tag != null ? tag.getString(ItemUtils.INNER_CORE_TAG_NAME) : null; - } - return null; - } - - public void setAllCustomData(String extra) { - customData = new JSONObject(extra); - if (item != null) { - CompoundTag tag = item.getOrCreateNamedTag(); - if (!customData.isEmpty()) { - tag.putString(ItemUtils.INNER_CORE_TAG_NAME, customData.toString()); - } else { - tag.remove(ItemUtils.INNER_CORE_TAG_NAME); - } - item.setNamedTag(tag); - } - } - - public String getCustomName() { - return item != null ? item.getCustomName() : ""; - } - - public void setCustomName(String name) { - if (item != null) { - item.setCustomName(name); - } - } - - public NativeCompoundTag getCompoundTag() { - CompoundTag tag = item != null ? item.getNamedTag() : null; - return tag != null ? new NativeCompoundTag(tag) : null; } - public void setCompoundTag(NativeCompoundTag tag) { - if (item != null) { - item.setNamedTag(tag.tag); - applyCustomDataJSON(); - } + public NativeItemInstanceExtra(ItemInstanceExtra extra) { + super(extra); } - private JSONObject customData = null; - private boolean customDataLoaded = false; - - private JSONObject getCustomDataJSON() { - if (!customDataLoaded) { - String raw = getAllCustomData(); - if (raw != null) { - try { - customData = new JSONObject(raw); - } catch (JSONException e) { - customData = null; - } - } - customDataLoaded = true; - } - return customData; - } - - private JSONObject getOrCreateCustomDataJSON() { - JSONObject data = getCustomDataJSON(); - if (data == null) { - data = customData = new JSONObject(); - } - return data; - } - - private void applyCustomDataJSON() { - if (customData != null) { - setAllCustomData(customData.toString()); - } else { - setAllCustomData(null); - } - } - - private NativeItemInstanceExtra putObject(String name, Object value) { - JSONObject data = getOrCreateCustomDataJSON(); - try { - data.put(name, value); - applyCustomDataJSON(); - } catch (JSONException e) { - e.printStackTrace(); - } - return this; - } - - public NativeItemInstanceExtra putString(String name, String value) { - return putObject(name, value); - } - - public NativeItemInstanceExtra putInt(String name, int value) { - return putObject(name, value); - } - - public NativeItemInstanceExtra putLong(String name, long value) { - return putObject(name, value); - } - - public NativeItemInstanceExtra putFloat(String name, double value) { - return putObject(name, value); - } - - public NativeItemInstanceExtra putBoolean(String name, boolean value) { - return putObject(name, value); - } - - public String getString(String name, String fallback) { - JSONObject data = getCustomDataJSON(); - if (data != null) { - return data.optString(name, fallback); - } - return fallback; - } - - public boolean contains(String name) { - JSONObject data = getCustomDataJSON(); - return data.opt(name) != null; - } - - public String getString(String name) { - return getString(name, null); - } - - public int getInt(String name, int fallback) { - JSONObject data = getCustomDataJSON(); - if (data != null) { - return data.optInt(name, fallback); - } - return fallback; - } - - public int getInt(String name) { - return getInt(name, 0); - } - - public long getLong(String name, long fallback) { - JSONObject data = getCustomDataJSON(); - if (data != null) { - return data.optLong(name, fallback); - } - return fallback; - } - - public long getLong(String name) { - return getLong(name, 0); - } - - public double getFloat(String name, double fallback) { - JSONObject data = getCustomDataJSON(); - if (data != null) { - return data.optDouble(name, fallback); - } - return fallback; - } - - public double getFloat(String name) { - return getFloat(name, 0); - } - - public boolean getBoolean(String name, boolean fallback) { - JSONObject data = getCustomDataJSON(); - if (data != null) { - return data.optBoolean(name, fallback); - } - return fallback; - } - - public boolean getBoolean(String name) { - return getBoolean(name, false); - } - - public NativeItemInstanceExtra putSerializable(String name, Object obj) { - return putString(name, ScriptableSerializer.jsonToString(ScriptableSerializer.scriptableToJson(obj, null))); - } - - public Object getSerializable(String name) { - try { - return ScriptableSerializer.scriptableFromJson(ScriptableSerializer.stringToJson(getString(name))); - } catch (JSONException e) { - return null; - } - } - - public void removeCustomData() { - customData = null; - customDataLoaded = false; - setAllCustomData(null); - } - - @Override - public String toString() { - return "ItemExtra{json=" + asJson() + "}"; - } - - private static Item constructNew() { - return Item.AIR_ITEM.clone(); - } - - @Deprecated(since = "Zote") - public static long constructClone(long ptr) { - throw new UnsupportedOperationException(); - } - - public static Item constructClone(Item item) { - if (item == null) { - Logger.error("NativeItemInstanceExtra", - "Received null instead of item, new extra will be constructed"); - return Item.AIR_ITEM.clone(); - } - return item.clone(); - } - - public static NativeItemInstanceExtra unwrapObject(Object extra) { - if (extra instanceof Wrapper) { - extra = ((Wrapper) extra).unwrap(); - } - if (extra instanceof NativeItemInstanceExtra) { - return (NativeItemInstanceExtra) extra; - } - if (extra instanceof Item) { - return new NativeItemInstanceExtra((Item) extra); - } - return null; - } - - public static Item unwrapValue(Object extra) { - if (extra == null) { - return null; - } - if (extra instanceof Wrapper) { - extra = ((Wrapper) extra).unwrap(); - } - if (extra instanceof NativeItemInstanceExtra) { - return ((NativeItemInstanceExtra) extra).getValue(); - } - if (extra instanceof Item) { - return (Item) extra; - } - return null; - } - - public static Item getValueOrNullPtr(NativeItemInstanceExtra extra) { - return extra != null ? extra.getValue() : null; - } - - @Deprecated(since = "Zote") - public static NativeItemInstanceExtra getExtraOrNull(long extra) { - throw new UnsupportedOperationException(); - } - - public static NativeItemInstanceExtra getExtraOrNull(Item extra) { - return extra != null ? new NativeItemInstanceExtra(extra) : null; - } - - public static NativeItemInstanceExtra cloneExtra(NativeItemInstanceExtra extra) { - Item item = getValueOrNullPtr(extra); - return item != null ? new NativeItemInstanceExtra(constructClone(item)) : null; - } - - public static NativeItemInstanceExtra fromJson(JSONObject json) { - if (json == null || json.length() == 0) { - return null; - } - - NativeItemInstanceExtra extra = new NativeItemInstanceExtra(); - JSONArray enchants = json.optJSONArray("enchants"); - if (enchants != null) { - for (int i = 0; i < enchants.length(); i++) { - JSONObject enchant = enchants.optJSONObject(i); - extra.addEnchant(enchant.optInt("id"), enchant.optInt("l")); - } - } - String name = json.optString("name"); - if (name != null) { - extra.setCustomName(name); - } - String data = json.optString("data"); - if (data != null) { - extra.setAllCustomData(data); - } - return extra; - } - - private int id; - - @Override - public int getId() { - return id; - } - - @Override - public void setId(int id) { - this.id = id; + public NativeItemInstanceExtra(NativeItemInstanceExtra extra) { + super(extra); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeItemModel.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeItemModel.java index bd7777642..c6816007f 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeItemModel.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeItemModel.java @@ -1,7 +1,7 @@ package com.zhekasmirnov.innercore.api; import android.graphics.Bitmap; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; +import com.reider745.api.ClientOnly; import com.zhekasmirnov.innercore.api.mod.adaptedscript.AdaptedScriptAPI.ICRender; import com.zhekasmirnov.innercore.api.mod.ui.GuiBlockModel; import com.zhekasmirnov.innercore.api.mod.ui.GuiRenderMesh; @@ -9,6 +9,8 @@ import com.zhekasmirnov.innercore.api.unlimited.BlockRegistry; import com.zhekasmirnov.innercore.api.unlimited.BlockVariant; import com.zhekasmirnov.innercore.mod.resource.ResourcePackManager; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; import org.mozilla.javascript.Context; import org.mozilla.javascript.annotations.JSStaticFunction; @@ -509,7 +511,8 @@ public interface IOverrideCallback { private IOverrideCallback mModelOverrideCallback = null; - public NativeItemModel getModelForItemInstance(int id, int count, int data, NativeItemInstanceExtra extra) { + @ClientOnly + public NativeItemModel getModelForItemInstance(int id, int count, int data, ItemInstanceExtra extra) { if (mModelOverrideCallback != null) { return mModelOverrideCallback.overrideModel(new ItemInstance(id, count, data, extra)); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeScales.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeScales.java index c9be36a5d..3f3c8544d 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeScales.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeScales.java @@ -5,6 +5,7 @@ import com.zhekasmirnov.apparatus.multiplayer.Network; import com.zhekasmirnov.apparatus.multiplayer.server.ConnectedClient; import com.zhekasmirnov.innercore.api.runtime.saver.world.WorldDataScopeRegistry; +import org.innercore.icstd.serialization.*; import org.json.JSONObject; import org.mozilla.javascript.annotations.JSStaticFunction; @@ -49,38 +50,36 @@ public class NativeScales { WorldDataScopeRegistry.getInstance().addScope("_ic.scales", new WorldDataScopeRegistry.SaverScope() { @Override - public void readJson(Object data) throws Exception { - if(data instanceof JSONObject) { - final JSONObject json = (JSONObject) data; - - for(Iterator it = json.keys(); it.hasNext(); ) { - final String mobKey = it.next(); - final long mob = Long.parseLong(mobKey); - final JSONObject scales = json.getJSONObject(mobKey); - - for(Iterator it2 = scales.keys(); it2.hasNext(); ) { - final String scaleId = it2.next(); - setScaleValue(mob, scaleId, scales.getDouble(scaleId)); + public void read(IScopedReader io, SerializationContext ctx) { + IObjectReader allScales = io.asObject(); + if (allScales == null) { + io.endScope(); + return; + } + for (String mobKey : allScales.iterKeys()) { + final long mob = Long.parseLong(mobKey); + IObjectReader mobScales = allScales.beginScope(mobKey).asObject(); + if (mobScales != null) { + for (String scaleId : mobScales.iterKeys()) { + setScaleValue(mob, scaleId, mobScales.getDouble(scaleId, 0.)); } } + allScales.endChildScope(); } + io.endScope(); } @Override - public Object saveAsJson() throws Exception { - final JSONObject json = new JSONObject(); - - for(long mob : getAllMobs()) { - final JSONObject mobJson = new JSONObject(); - - for(String scaleId : getAllScales()) { - mobJson.put(scaleId, getScaleValue(mob, scaleId)); + public void write(IScopedWriter io, SerializationContext ctx) { + IObjectWriter allScales = io.asObject(); + for (long mob : getAllMobs()) { + IObjectWriter mobScales = allScales.beginScope(String.valueOf(mob)).asObject(); + for (String scaleId : getAllScales()) { + mobScales.putDouble(scaleId, getScaleValue(mob, scaleId)); } - - json.put(String.valueOf(mob), mobJson); + mobScales.endScope(); } - - return json; + allScales.endScope(); } }); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/NativeTileEntity.java b/src/main/java/com/zhekasmirnov/innercore/api/NativeTileEntity.java index 1dae0f069..fc5e3b66b 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/NativeTileEntity.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/NativeTileEntity.java @@ -5,8 +5,6 @@ import com.reider745.InnerCoreServer; import com.reider745.hooks.item.ItemUtils; -import com.reider745.world.BlockSourceMethods; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; import com.zhekasmirnov.apparatus.minecraft.enums.GameEnums; import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.api.nbt.NativeCompoundTag; @@ -16,12 +14,17 @@ import cn.nukkit.inventory.InventoryHolder; import cn.nukkit.item.Item; import cn.nukkit.nbt.tag.CompoundTag; +import org.innercore.icstd.blockentity.BlockEntityComponent; +import org.innercore.icstd.blockentity.BlockEntityType; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.scene.GameObjectHandle; /** * Created by zheka on 26.07.2017. */ -public class NativeTileEntity { +public class NativeTileEntity implements BlockEntityComponent { public static final boolean isContainer = false; private BlockEntity blockEntity; @@ -59,13 +62,13 @@ public NativeItemInstance getSlot(int slot) { public void setSlot(int slot, int id, int count, int data, Object extra) { if (slot < 0 || slot >= size) return; - setSlot(blockEntity, slot, id, count, data, NativeItemInstanceExtra.unwrapObject(extra)); + setSlot(blockEntity, slot, id, count, data, ItemInstanceExtra.unwrapObject(extra)); } public void setSlot(int slot, int id, int count, int data) { if (slot < 0 || slot >= size) return; - setSlot(blockEntity, slot, id, count, data, (NativeItemInstanceExtra) null); + setSlot(blockEntity, slot, id, count, data, (ItemInstanceExtra) null); } public void setSlot(int slot, NativeItemInstance item) { @@ -90,7 +93,7 @@ public void tick() { @Deprecated(since = "Zote") public static NativeTileEntity getTileEntity(int x, int y, int z) { - return NativeBlockSource.getCurrentWorldGenRegion().getBlockEntity(x, y, z); + return BlockSource.getCurrentWorldGenRegion().getNativeBlockEntity(x, y, z); } /* @@ -165,10 +168,10 @@ public static Item getSlot(BlockEntity blockEntity, int slot) { } public static void setSlot(BlockEntity blockEntity, int slot, int id, int count, int data, Item extra) { - setSlot(blockEntity, slot, id, count, data, NativeItemInstanceExtra.getExtraOrNull(extra)); + setSlot(blockEntity, slot, id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); } - public static void setSlot(BlockEntity blockEntity, int slot, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setSlot(BlockEntity blockEntity, int slot, int id, int count, int data, ItemInstanceExtra extra) { Item item = ItemUtils.get(id, count, data, extra); if (blockEntity instanceof InventoryHolder holder) { holder.getInventory().setItem(slot, item); @@ -206,7 +209,11 @@ public static void setCompoundTag(BlockEntity blockEntity, CompoundTag tag) { @Deprecated(since = "Zote") public static BlockEntity getInWorld(int x, int y, int z) { - return BlockSourceMethods.getBlockEntity(NativeBlockSource.getCurrentWorldGenRegion().getPointer(), x, y, z); + BlockSource source = BlockSource.getCurrentWorldGenRegion(); + if(source == null) return null; + + NativeTileEntity tileEntity = source.getNativeBlockEntity(x, y, z); + return tileEntity != null ? tileEntity.blockEntity : null; } @Deprecated(since = "Zote") @@ -228,4 +235,14 @@ public static void setSlot2(BlockEntity pointer, int slot, long itemInstance) { public static void setCompoundTag(BlockEntity pointer, long tag) { InnerCoreServer.useNotSupport("NativeTileEntity.setCompoundTag(pointer, tag)"); } + + @Override + public GameObjectHandle getObjectHandle() { + return null; + } + + @Override + public BlockEntityType getBlockEntityType() { + return null; + } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/commontypes/Coords.java b/src/main/java/com/zhekasmirnov/innercore/api/commontypes/Coords.java deleted file mode 100644 index 5fc02bdee..000000000 --- a/src/main/java/com/zhekasmirnov/innercore/api/commontypes/Coords.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.zhekasmirnov.innercore.api.commontypes; - -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import org.mozilla.javascript.ScriptableObject; - -/** - * Created by zheka on 09.08.2017. - */ - -public class Coords extends ScriptableObject { - - @Override - public String getClassName() { - return "Coords"; - } - - public Coords(int x, int y, int z, int side, boolean relativeCoordsAreSame) { - super(); - - int relX = x, relY = y, relZ = z; - if (!relativeCoordsAreSame) { - switch (side) { - case 0: - relY--; - break; - case 1: - relY++; - break; - case 2: - relZ--; - break; - case 3: - relZ++; - break; - case 4: - relX--; - break; - case 5: - relX++; - break; - } - } - - put("x", this, x); - put("y", this, y); - put("z", this, z); - put("side", this, side); - - ScriptableObject rel = ScriptableObjectHelper.createEmpty(); - rel.put("x", rel, relX); - rel.put("y", rel, relY); - rel.put("z", rel, relZ); - put("relative", this, rel); - } - - public Coords(int x, int y, int z, int side) { - this(x, y, z, side, false); - } - - public Coords(double x, double y, double z) { - super(); - put("x", this, x); - put("y", this, y); - put("z", this, z); - } - - public Coords(int x, int y, int z) { - super(); - put("x", this, x); - put("y", this, y); - put("z", this, z); - } - - public Coords setSide(int side) { - put("side", this, side); - return this; - } -} diff --git a/src/main/java/com/zhekasmirnov/innercore/api/commontypes/FullBlock.java b/src/main/java/com/zhekasmirnov/innercore/api/commontypes/FullBlock.java deleted file mode 100644 index 7978fc4b7..000000000 --- a/src/main/java/com/zhekasmirnov/innercore/api/commontypes/FullBlock.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.zhekasmirnov.innercore.api.commontypes; - -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; -import org.mozilla.javascript.ScriptableObject; - -/** - * Created by zheka on 09.08.2017. - */ - -public class FullBlock extends ScriptableObject { - public int id, data; - - @Override - public String getClassName() { - return "FullBlock"; - } - - public FullBlock(int id, int data) { - this.id = id; - put("id", this, this.id); - this.data = data; - put("data", this, this.data); - } - - public FullBlock(int idData) { - this.id = idData & 0xFFFF; - if (idData >> 24 == 1) { - this.id = -this.id; - } - put("id", this, this.id); - this.data = (idData >> 16) & 0xFF; - put("data", this, this.data); - } - - public FullBlock(NativeBlockSource blockSource, int x, int y, int z) { - if (blockSource != null) { - this.id = blockSource.getBlockId(x, y, z); - this.data = blockSource.getBlockData(x, y, z); - } else { - id = data = 0; - } - put("id", this, this.id); - put("data", this, this.data); - } - - public FullBlock(long actor, int x, int y, int z) { - this(NativeBlockSource.getDefaultForActor(actor), x, y, z); - } -} diff --git a/src/main/java/com/zhekasmirnov/innercore/api/commontypes/ItemInstance.java b/src/main/java/com/zhekasmirnov/innercore/api/commontypes/ItemInstance.java index 5d918b2a6..d402f95a7 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/commontypes/ItemInstance.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/commontypes/ItemInstance.java @@ -1,65 +1,33 @@ package com.zhekasmirnov.innercore.api.commontypes; -import com.zhekasmirnov.innercore.api.NativeItemInstance; import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; - -import cn.nukkit.item.Item; - -import org.mozilla.javascript.ScriptableObject; - -/** - * Created by zheka on 09.08.2017. - */ - -public class ItemInstance extends ScriptableObject { - - @Override - public String getClassName() { - return "Item"; - } - - public ItemInstance(int id, int count, int data) { - put("id", this, id); - put("count", this, count); - put("data", this, data); - } +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.mozilla.javascript.Scriptable; +// this is a stub class for DungeonUtility, it MUST NOT be used in code +@Deprecated +public class ItemInstance extends org.innercore.icstd.inventory.ItemInstance { public ItemInstance(int id, int count, int data, NativeItemInstanceExtra extra) { - this(id, count, data); - put("extra", this, extra); - } - - public ItemInstance(NativeItemInstance nativeItemInstance) { - this(nativeItemInstance.id, nativeItemInstance.count, nativeItemInstance.data, nativeItemInstance.extra); - } - - public ItemInstance(Item item) { - this(new NativeItemInstance(item)); - } - - @Deprecated - public ItemInstance(long ptr) { - throw new UnsupportedOperationException("ItemInstance(ptr)"); + super(id, count, data, extra); } - public int getId() { - return ((Number) get("id")).intValue(); + public ItemInstance(int id, int count, int data, ItemInstanceExtra extra) { + super(id, count, data, extra); } - public int getCount() { - return ((Number) get("count")).intValue(); + public ItemInstance(int id, int count, int data) { + super(id, count, data); } - public int getData() { - return ((Number) get("data")).intValue(); + public ItemInstance(org.innercore.icstd.inventory.ItemInstance rhs) { + super(rhs); } - public NativeItemInstanceExtra getExtra() { - return NativeItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(this, "extra", null)); + public ItemInstance() { + super(); } - public Item getExtraValue() { - return NativeItemInstanceExtra.unwrapValue(ScriptableObjectHelper.getProperty(this, "extra", null)); + public ItemInstance(Scriptable scriptable) { + super(scriptable); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/dimensions/CustomDimension.java b/src/main/java/com/zhekasmirnov/innercore/api/dimensions/CustomDimension.java index 7a06ead44..38ad6bf27 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/dimensions/CustomDimension.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/dimensions/CustomDimension.java @@ -1,8 +1,10 @@ package com.zhekasmirnov.innercore.api.dimensions; import com.reider745.InnerCoreServer; +import com.reider745.api.ClientOnly; import com.reider745.world.dimensions.DimensionsMethods; import com.zhekasmirnov.innercore.api.log.ICLog; +import org.innercore.icstd.modules.DimensionsModule; import java.util.HashMap; @@ -42,12 +44,62 @@ public CustomDimensionGenerator getGenerator() { private boolean hasSkyLight = true; + public CustomDimension setEvaporatesLiquids(boolean value) { + DimensionsModule.setEvaporatesLiquids(id, value); + return this; + } + + public CustomDimension setWeatherSeasons(boolean value) { + DimensionsModule.setWeatherSeasons(id, value); + return this; + } + public CustomDimension setHasSkyLight(boolean value) { InnerCoreServer.useNotCurrentSupport("CustomDimension.setHasSkyLight(value)"); this.hasSkyLight = value; return this; } + public CustomDimension setBedExplodesOnSleep(boolean value) { + InnerCoreServer.useNotCurrentSupport("CustomDimension.setBedExplodesOnSleep(value)"); + return this; + } + + public CustomDimension setSkyAtmosphereEnabled(boolean value) { + InnerCoreServer.useNotCurrentSupport("CustomDimension.setSkyAtmosphereEnabled(value)"); + return this; + } + + @ClientOnly + public CustomDimension setCloudHeight(int height) { + return this; + } + + public CustomDimension setTimeOfDay(float interval) { + InnerCoreServer.useNotCurrentSupport("CustomDimension.setTimeOfDay(value)"); + return this; + } + + public CustomDimension setTimeOfDayAmplifier(float amplifier) { + InnerCoreServer.useNotCurrentSupport("CustomDimension.setTimeOfDay(value)"); + return this; + } + + public CustomDimension resetTimeOfDay() { + InnerCoreServer.useNotCurrentSupport("CustomDimension.resetTimeOfDay(value)"); + return this; + } + + public CustomDimension setStarBrightness(float interval) { + InnerCoreServer.useNotCurrentSupport("CustomDimension.setStarBrightness(value)"); + return this; + } + + public CustomDimension resetStarBrightness() { + InnerCoreServer.useNotCurrentSupport("CustomDimension.resetStarBrightness(value)"); + return this; + } + public boolean hasSkyLight() { return hasSkyLight; } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/entities/NativePathNavigation.java b/src/main/java/com/zhekasmirnov/innercore/api/entities/NativePathNavigation.java index 9c10e7046..95f367643 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/entities/NativePathNavigation.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/entities/NativePathNavigation.java @@ -1,297 +1,14 @@ package com.zhekasmirnov.innercore.api.entities; -import java.util.HashMap; +import org.innercore.icstd.entity.PathNavigation; -import com.reider745.InnerCoreServer; - -@SuppressWarnings("unused") -public class NativePathNavigation { - private final long entity; - private boolean isValid = true; - - private static HashMap activeNavigationMap = new HashMap<>(); - - public static NativePathNavigation getNavigation(long entity) { - NativePathNavigation result = activeNavigationMap.get(entity); - if (result != null) { - return result; - } - result = new NativePathNavigation(entity); - activeNavigationMap.put(entity, result); - return result; - } - - public static void onNavigationResult(long entity, int result) { - NativePathNavigation navigation = getNavigation(entity); - if (navigation.resultFunction != null) { - navigation.resultFunction.onNavigationResult(navigation, result); - } - } - - private NativePathNavigation(long entity) { - this.entity = entity; - // TODO: if (pointer == 0) { - // throw new IllegalArgumentException("Invalid mob was passed to NativePathNavigation constructor: " + entity); - // } - } - - public long getEntity() { - return entity; - } - - public static void cleanup() { - for (NativePathNavigation navigation : activeNavigationMap.values()) { - navigation.isValid = false; - } - activeNavigationMap.clear(); - } - - interface NavigationResultFunction { - void onNavigationResult(NativePathNavigation navigation, int result); - } - - NavigationResultFunction resultFunction; - - public NativePathNavigation setResultFunction(NavigationResultFunction resultFunction) { - if (this.resultFunction != null) { - this.resultFunction.onNavigationResult(this, 6); // FUNCTION_REPLACED - } - this.resultFunction = resultFunction; - return this; - } - - public NativePathNavigation moveToCoords(float x, float y, float z, float velocity) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.moveToCoords(x, y, z, velocity)"); - return this; - } - - public NativePathNavigation moveToEntity(long entity, float velocity) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.moveToEntity(entity, velocity)"); - return this; - } - - public NativePathNavigation setMaxNavigationDistance(float distance) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setMaxNavigationDistance(distance)"); - return this; - } - - public float getMaxNavigationDistance() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getMaxNavigationDistance()"); - return 0; - } - - public NativePathNavigation stop() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.stop()"); - return this; - } - - public NativePathNavigation setType(int type) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setType(type)"); - return this; - } - - public boolean isDone() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.isDone()"); - return false; - } - - public boolean canPassDoors() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.canPassDoors()"); - return false; - } - - public NativePathNavigation setCanPassDoors(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanPassDoors(value)"); - return this; - } - - public boolean isRiverFollowing() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.isRiverFollowing()"); - return false; - } - - public NativePathNavigation setIsRiverFollowing(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setIsRiverFollowing(value)"); - return this; - } - - public boolean canOpenDoors() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.canOpenDoors()"); - return false; - } - - public NativePathNavigation setCanOpenDoors(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanOpenDoors(value)"); - return this; - } - - public boolean getAvoidSun() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidSun()"); - return false; - } - - public NativePathNavigation setAvoidSun(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidSun(value)"); - return this; - } - - public boolean getAvoidWater() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidWater()"); - return false; - } - - public NativePathNavigation setAvoidWater(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidWater(value)"); - return this; - } - - public NativePathNavigation setEndPathRadius(float value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setEndPathRadius(value)"); - return this; - } - - public boolean getCanSink() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanSink()"); - return false; - } - - public NativePathNavigation setCanSink(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanSink(value)"); - return this; - } - - public boolean getAvoidDamageBlocks() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidDamageBlocks()"); - return false; - } - - public NativePathNavigation setAvoidDamageBlocks(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidDamageBlocks(value)"); - return this; - } - - public boolean getCanFloat() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanFloat()"); - return false; - } - - public NativePathNavigation setCanFloat(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanFloat(value)"); - return this; - } - - public boolean isAmphibious() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.isAmphibious()"); - return false; - } - - public NativePathNavigation setIsAmphibious(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setIsAmphibious(value)"); - return this; - } - - public boolean getAvoidPortals() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidPortals()"); - return false; - } - - public NativePathNavigation setAvoidPortals(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidPortals(value)"); - return this; - } - - public boolean getCanBreach() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanBreach()"); - return false; - } - - public NativePathNavigation setCanBreach(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanBreach(value)"); - return this; - } - - public boolean getCanJump() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanJump()"); - return false; - } - - public NativePathNavigation setCanJump(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanJump(value)"); - return this; - } - - public float getSpeed() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getSpeed()"); - return 0; - } - - public NativePathNavigation setSpeed(float value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setSpeed(value)"); - return this; - } - - public boolean getCanPathOverLava() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanPathOverLava()"); - return false; - } - - public NativePathNavigation setCanPathOverLava(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanPathOverLava(value)"); - return this; - } - - public boolean getCanWalkInLava() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanWalkInLava()"); - return false; - } - - public NativePathNavigation setCanWalkInLava(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanWalkInLava(value)"); - return this; - } - - public boolean getCanOpenIronDoors() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanOpenIronDoors()"); - return false; - } - - public NativePathNavigation setCanOpenIronDoors(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanOpenIronDoors(value)"); - return this; - } - - public boolean getHasEndPathRadius() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getHasEndPathRadius()"); - return false; - } - - public NativePathNavigation setHasEndPathRadius(boolean value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setHasEndPathRadius(value)"); - return this; - } - - public float getTerminationThreshold() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getTerminationThreshold()"); - return 0; - } - - public NativePathNavigation getTerminationThreshold(float value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getTerminationThreshold(value)"); - return this; - } - - public int getTickTimeout() { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getTickTimeout()"); - return 0; - } - - public NativePathNavigation setTickTimeout(int value) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setTickTimeout(value)"); - return this; +@Deprecated +public class NativePathNavigation extends PathNavigation { + public NativePathNavigation(long entity) { + super(entity); } - public boolean isStuck(int ticks) { - InnerCoreServer.useNotCurrentSupport("NativePathNavigation.isStuck(ticks)"); - return false; + public NativePathNavigation(long ptr, int refCnt, Object stub) { + super(ptr, refCnt, stub); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/log/DialogHelper.java b/src/main/java/com/zhekasmirnov/innercore/api/log/DialogHelper.java index 30da6f85a..eb50d49a3 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/log/DialogHelper.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/log/DialogHelper.java @@ -1,86 +1,72 @@ package com.zhekasmirnov.innercore.api.log; -import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.apparatus.adapter.innercore.UserDialog; +import com.zhekasmirnov.innercore.api.InnerCoreConfig; +import com.zhekasmirnov.innercore.utils.UIUtils; + +import java.util.List; /** * Created by zheka on 16.09.2017. */ public class DialogHelper { + public static String getFormattedLog(int limit) { + return ICLog.getLogFilter().buildFilteredLog(true, limit); + } + public static String getFormattedLog() { - return ICLog.getLogFilter().buildFilteredLog(true); + return getFormattedLog(InnerCoreConfig.getInt("logging.dialog_entries_limit", 32)); } public static String getFormattedStackTrace(Throwable error) { - return ICLog.getStackTrace(error); + return "" + ICLog.getStackTrace(error) + ""; + } + + public static void openFormattedDialog(final String text, final String title, int maxCount, Runnable dialogOverflowCallback, Runnable dialogCloseCallback) { + } - public static final int DEFAULT_MAX_COUNT = 5; - private static int totalDialogCount = 0; + public static void openFormattedDialog(final String text, final String title, int maxCount, Runnable dialogOverflowCallback) { - public static void openFormattedDialog(final String text, final String title, int maxCount, - Runnable dialogOverflowCallback) { - Logger.info(title, text); } public static void openFormattedDialog(String text, String title) { - Logger.info(title, text); + } public static int getTotalDialogCount() { - return totalDialogCount; + return 0; } public interface ICustomErrorCallback { boolean show(String message, Throwable error, String log, String stacktrace); - } - - private static ICustomErrorCallback customFatalErrorCallback = null; + }; public static void setCustomFatalErrorCallback(ICustomErrorCallback customFatalErrorCallback) { - DialogHelper.customFatalErrorCallback = customFatalErrorCallback; } public static void reportFatalError(String message, Throwable error) { - String stacktrace = getFormattedStackTrace(error); - String log = getFormattedLog(); - if (customFatalErrorCallback != null && customFatalErrorCallback.show(message, error, log, stacktrace)) { - return; - } - String text = message + "\n\n\nSTACKTRACE:\n" + stacktrace + "\n\n\nLOG:\n" + log; - openFormattedDialog(text, "FATAL ERROR"); - } - private static ICustomErrorCallback customNonFatalErrorCallback = null; + } public static void setCustomNonFatalErrorCallback(ICustomErrorCallback customNonFatalErrorCallback) { - DialogHelper.customNonFatalErrorCallback = customNonFatalErrorCallback; + } public static void reportNonFatalError(String message, Throwable error) { - String stacktrace = getFormattedStackTrace(error); - if (customNonFatalErrorCallback != null - && customNonFatalErrorCallback.show(message, error, getFormattedLog(), stacktrace)) { - return; - } - - String text = message + "\n\n\nSTACKTRACE:\n" + stacktrace; - openFormattedDialog(text, "NON-FATAL ERROR"); + } - private static ICustomErrorCallback customStartupErrorCallback = null; + public static void reportNonFatalErrorList(String message, List errors) { + + } public static void setCustomStartupErrorCallback(ICustomErrorCallback customStartupErrorCallback) { - DialogHelper.customStartupErrorCallback = customStartupErrorCallback; + } public static void reportStartupErrors(String message) { - String log = getFormattedLog(); - if (customStartupErrorCallback != null && customStartupErrorCallback.show(message, null, log, null)) { - return; - } - String text = message + "\n\n\nLOG:\n" + log; - openFormattedDialog(text, "NON-FATAL ERROR"); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/log/ICLog.java b/src/main/java/com/zhekasmirnov/innercore/api/log/ICLog.java index cd31eca70..6d8763464 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/log/ICLog.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/log/ICLog.java @@ -2,32 +2,40 @@ import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.utils.FileTools; +import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.WrappedException; + import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.HashMap; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ScriptStackElement; + /** * Created by zheka on 09.08.2017. */ public class ICLog { - private static LogFilter logFilter; + public static int INLINE_STACKTRACE_LINES_LIMIT = 5; + private static LogFilter logFilter = new LogFilter(); private static LogWriter logWriter; + private static boolean hasEvenSeenRhinoErrors = false; public static LogFilter getLogFilter() { - if (logFilter == null) { - logFilter = new LogFilter(); - } return logFilter; } public static LogWriter getLogWriter() { - if (logWriter == null) { - logWriter = new LogWriter(new File(FileTools.DIR_WORK + "inner-core.log")); - logWriter.clear(); - } return logWriter; } + static { + logWriter = new LogWriter(new File(FileTools.DIR_WORK + "inner-core.log")); + logWriter.clear(); + } + private static HashMap eventHandlerForThread = new HashMap<>(); private static long getThreadId() { @@ -42,17 +50,14 @@ public static IEventHandler getEventHandlerForCurrentThread() { return eventHandlerForThread.get(getThreadId()); } + private static String removeFaultSymbolsFromString(String s) { return s.replaceAll("%", "%%"); } private static void logMsg(LogType type, String prefix, String msg) { - if (logFilter != null) { - logFilter.log(type, prefix, msg); - } - if (logWriter != null) { - logWriter.logMsg(type, prefix, msg); - } + logFilter.log(type, prefix, msg); + logWriter.logMsg(type, prefix, msg); } public static void l(String prefix, String message) { @@ -80,30 +85,133 @@ public static void i(String prefix, String message) { } logMsg(LogType.IMPORTANT, prefix, message); if (prefix.toUpperCase().equals("ERROR")) { - Logger.error(prefix, removeFaultSymbolsFromString(message)); + String stacktrace = captureRhinoStackTrace(INLINE_STACKTRACE_LINES_LIMIT, true); + Logger.error(prefix, removeFaultSymbolsFromString(message + (stacktrace != null ? " (" + stacktrace + ")" : ""))); } else { Logger.info(prefix, removeFaultSymbolsFromString(message)); } } + public static void e(String prefix, String message) { + e(prefix, message, null); + } + public static void e(String prefix, String message, Throwable err) { IEventHandler handler = getEventHandlerForCurrentThread(); if (handler != null) { handler.onErrorEvent(prefix, message, err); } - logMsg(LogType.ERROR, "ERROR", - (prefix != null ? "[" + prefix + "] " : "") + message + (err != null ? "\n" + getStackTrace(err) : "")); - Logger.error(prefix, removeFaultSymbolsFromString(message + (err != null ? "\n" + getStackTrace(err) : ""))); + + String stacktrace = err != null ? "\n" + getStackTrace(err) : null; + if (stacktrace == null) { + stacktrace = captureRhinoStackTrace(INLINE_STACKTRACE_LINES_LIMIT, true); + if (stacktrace != null) { + stacktrace = " (" + stacktrace + ")"; + } + } + logMsg(LogType.ERROR, "ERROR", (prefix != null ? "[" + prefix + "] " : "") + message + (stacktrace != null ? stacktrace : "")); + Logger.error(prefix, removeFaultSymbolsFromString(message + (stacktrace != null ? stacktrace : ""))); } public static String getStackTrace(Throwable err) { - return Logger.getStackTrace(err); + return exceptionToString(err, false, false, -1, -1); } - public static void flush() { - if (logWriter != null) { - logWriter.flush(); + public static String exceptionToString(Throwable err, boolean inline, boolean originalCauseOnly, int javaFrameLimit, int jsFrameLimit) { + String jsStack = captureRhinoStackTrace(err, jsFrameLimit, inline); + if (err instanceof WrappedException && ((WrappedException) err).getWrappedException() != null) { + err = ((WrappedException) err).getWrappedException(); + } + if (originalCauseOnly) { + while (err.getCause() != null) { + err = err.getCause(); + } + } + if (inline) { + StackTraceElement[] stack = err.getStackTrace(); + return err.getMessage() + " " + jsStack + " " + (stack.length > 0 ? stack[stack.length - 1] : ""); + } else { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + try { + // todo: actually limit stack trace + if (javaFrameLimit != 0) { + err.printStackTrace(pw); + } else { + pw.print(err); + } + } catch (Throwable th) { + pw.write(""); + } + if (jsStack != null) { + return "JS STACK TRACE:\n" + jsStack + "\n\nFULL STACK TRACE:\n" + sw.toString(); + } else { + return sw.toString(); + } + } + } + + public static String captureRhinoStackTrace(Throwable th, int limit, boolean inlined) { + try { + if (th instanceof RhinoException) { + return getRhinoStackTrace((RhinoException) th, limit, inlined); + } + try { + throw Context.throwAsScriptRuntimeEx(th != null ? th : new RuntimeException()); + } catch (RhinoException exc) { + return getRhinoStackTrace(exc, limit, inlined); + } catch (RuntimeException ignore) { + } + } catch (Throwable th2) { + ICLog.i("ERROR", "captureRhinoStackTrace failed with error " + th); // note: avoid recursion } + return null; + } + + public static String captureRhinoStackTrace(int limit, boolean inlined) { + return captureRhinoStackTrace(null, limit, inlined); + } + + private static String getRhinoStackTrace(RhinoException exc, int limit, boolean inlined) { + ScriptStackElement[] stack = null; + try { + stack = exc.getScriptStack(limit <= 0 ? -1 : limit, null); + } catch (Throwable th) { + if (!hasEvenSeenRhinoErrors) { + Logger.error("InnerCore-Log", "FATAL ERROR occurred while attempting to retrieve stacktrace; following errors will be ignored: " + th.getMessage()); + hasEvenSeenRhinoErrors = true; + } + } + + if (stack == null) { + return ""; + } else if (stack.length != 0) { + StringBuilder sb = new StringBuilder(); + for (ScriptStackElement element : stack) { + if (sb.length() != 0) { + sb.append(inlined ? ", " : "\n"); + } else if (inlined) { + sb.append("at "); + } + + if (!inlined) { + sb.append("\tat "); + } + sb.append(element.fileName); + if (element.lineNumber >= 0) { + sb.append(':').append(element.lineNumber); + } + if (element.functionName != null) { + sb.append(" (").append(element.functionName).append(')'); + } + } + return sb.toString(); + } + return null; + } + + public static void flush() { + logWriter.flush(); } public static void showIfErrorsAreFound() { @@ -111,4 +219,22 @@ public static void showIfErrorsAreFound() { DialogHelper.reportStartupErrors("Some errors are occured during Inner Core startup and loading."); } } + + private static long elapsedTimeStart = 0; + + public static void initElapsedTime() { + elapsedTimeStart = System.nanoTime(); + } + + public static double getElapsedTime() { + return (System.nanoTime() - elapsedTimeStart) / 1e9; + } + + public static double getElapsedTimeRound() { + return Math.round((System.nanoTime() - elapsedTimeStart) / 1e7) / 100.0; + } + + public static String timePrefix() { + return getElapsedTimeRound() + ": "; + } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/log/LogFilter.java b/src/main/java/com/zhekasmirnov/innercore/api/log/LogFilter.java index 8d300c97f..a0196fb49 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/log/LogFilter.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/log/LogFilter.java @@ -22,24 +22,23 @@ public void log(LogType type, String prefix, String line) { logMessages.add(new LogMessage(type, prefix, line)); } - private LogMessage getMessageByIndex(int index) { - if (index < 0 || index >= logMessages.size()) { - return null; + public String buildFilteredLog(boolean format, int limit) { + StringBuilder sb = new StringBuilder(); + + int index = limit >= 0 ? Math.max(0, logMessages.size() - limit) : 0; + for (; index < logMessages.size(); index++) { + sb.append(logMessages.get(index).format(format)); + sb.append(format ? "
" : "\n"); } - return logMessages.get(index); + + return sb.toString(); } public String buildFilteredLog(boolean format) { - String build = ""; - - for (int i = 0; i < logMessages.size(); i++) { - LogMessage msg = getMessageByIndex(i); - build += msg.format(format) + (format ? "
" : "\n"); - } - - return build; + return buildFilteredLog(format, -1); } + public static class LogMessage { public final LogType type; public final LogPrefix prefix; @@ -59,8 +58,7 @@ public String toString() { } public String toHtml() { - return "" + "[" + strPrefix + "]" + "" + " " + message - + ""; + return "" + "[" + strPrefix + "]" + "" + " " + message + ""; } public String format(boolean format) { diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/API.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/API.java index 03415ad70..db4dc219c 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/API.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/API.java @@ -4,6 +4,7 @@ import com.zhekasmirnov.innercore.api.annotations.APIIgnore; import com.zhekasmirnov.innercore.api.annotations.APIStaticModule; import com.zhekasmirnov.innercore.api.constants.ConstantRegistry; +import com.zhekasmirnov.innercore.api.gen.ApiModuleMap; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.adaptedscript.AdaptedScriptAPI; import com.zhekasmirnov.innercore.api.mod.adaptedscript.PreferencesWindowAPI; @@ -14,13 +15,12 @@ import com.zhekasmirnov.innercore.mod.build.Mod; import com.zhekasmirnov.innercore.mod.executable.Compiler; import com.zhekasmirnov.innercore.mod.executable.Executable; -import org.mozilla.javascript.Function; -import org.mozilla.javascript.NativeJavaClass; -import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.*; import org.mozilla.javascript.annotations.JSFunction; import org.mozilla.javascript.annotations.JSStaticFunction; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; @@ -73,6 +73,46 @@ protected void addExecutableCallback(Executable exec, String callbackName, Strin } } + @JSStaticFunction + public static void injectBindingsIntoScope(String moduleName, Scriptable object, boolean allowOverrides) { + Class moduleClass = ApiModuleMap.MODULES.get(moduleName); + if (moduleClass == null) { + Logger.error(LOGGER_TAG, "cannot inject bindings of " + moduleName + " into " + ScriptRuntime.toString(object) + " since module does not exist!"); + return; + } + try { + Method injectJsStaticMethod = moduleClass.getMethod("injectJsStatic", Scriptable.class, boolean.class); + injectJsStaticMethod.invoke(null, object, allowOverrides); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Cannot inject bindings of " + moduleName + " because invocation exception has thrown during call!", e); + } + } + + @JSStaticFunction + public static void injectModuleIntoScope(String moduleName, Scriptable scope, boolean allowOverrides) { + Scriptable module = ScriptableObjectHelper.getScriptableProperty(scope, moduleName, null); + if (module == null) { + Class moduleClass = ApiModuleMap.MODULES.get(moduleName); + if (moduleClass == null) { + Logger.error(LOGGER_TAG, "cannot inject bindings of " + moduleName + " into scope since module does not exist!"); + return; + } + try { + Method createEmptyModuleObjectMethod = moduleClass.getMethod("createEmptyModuleObject"); + module = (Scriptable) createEmptyModuleObjectMethod.invoke(null); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException("Cannot inject bindings of " + moduleName + " because invocation exception has thrown during call!", e); + } + } + injectBindingsIntoScope(moduleName, module, allowOverrides); + scope.put(moduleName, scope, module); + } + + @JSStaticFunction + public static void injectModulesIntoScope(Scriptable scope, boolean allowOverrides) { + ApiModuleMap.MODULES.forEach((name, module) -> injectModuleIntoScope(name, scope, allowOverrides)); + } + @JSStaticFunction public String getCurrentAPIName() { return getName(); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/AdaptedScriptAPI.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/AdaptedScriptAPI.java index 26a7dd018..0da7a50c5 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/AdaptedScriptAPI.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/AdaptedScriptAPI.java @@ -9,10 +9,10 @@ import com.reider745.api.ClientOnly; import com.reider745.entity.EntityMethod; import com.reider745.item.CustomItem; +import com.zhekasmirnov.apparatus.adapter.innercore.EngineConfig; import com.zhekasmirnov.apparatus.adapter.innercore.game.entity.EntityActor; import com.zhekasmirnov.apparatus.api.player.armor.ActorArmorHandler; import com.zhekasmirnov.apparatus.ecs.core.*; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; import com.zhekasmirnov.apparatus.mcpe.NativePlayer; import com.zhekasmirnov.apparatus.minecraft.version.MinecraftVersions; import com.zhekasmirnov.apparatus.multiplayer.Network; @@ -22,7 +22,6 @@ import com.zhekasmirnov.horizon.util.FileUtils; import com.zhekasmirnov.innercore.api.*; import com.zhekasmirnov.innercore.api.annotations.*; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; import com.zhekasmirnov.innercore.api.commontypes.ScriptableParams; import com.zhekasmirnov.innercore.api.constants.EntityType; import com.zhekasmirnov.innercore.api.dimensions.CustomDimensionGenerator; @@ -59,6 +58,10 @@ import com.zhekasmirnov.innercore.ui.LoadingUI; import com.zhekasmirnov.innercore.utils.FileTools; import com.zhekasmirnov.innercore.utils.UIUtils; +import org.innercore.icstd.entity.PathNavigation; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.innercore.icstd.serialization.SerializationContext; import org.json.JSONException; import org.mozilla.javascript.*; import org.mozilla.javascript.annotations.JSStaticFunction; @@ -106,6 +109,11 @@ public void setupCallbacks(Executable executable) { @Override public void prepareExecutable(Executable executable) { super.prepareExecutable(executable); + + if (isLoaded) { + ScriptableObject scope = executable.getScope(); + injectModulesIntoScope(scope, true); + } } @JSStaticFunction @@ -128,6 +136,13 @@ public static void logDeprecation(String functionName) { ICLog.d("WARNING", "using deprecated or unimplemented method " + functionName + "()"); } + @JSStaticFunction + public static void logIndevDeprecation(String functionName) { + if (EngineConfig.isDeveloperMode()) { + logDeprecation(functionName); + } + } + @JSStaticFunction @Deprecated(since = "Zote") public static void setTile(int x, int y, int z, int id, int data) { @@ -386,7 +401,7 @@ public static long dropItem(double x, double y, double z, int placeholder, int i return -1; } return NativeAPI.spawnDroppedItem((float) x, (float) y, (float) z, id, count, data, - NativeItemInstanceExtra.unwrapObject(extra)); + ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction @@ -641,8 +656,7 @@ public static void resetUnderwaterFogDistance() { @APIStaticModule public static class Entity { - - static long unwrapEntity(Object ent) { + public static long unwrapEntity(Object ent) { return (long) (ent instanceof Wrapper ? ((Wrapper) ent).unwrap() : ((Number) ent).longValue()); } @@ -902,62 +916,62 @@ public static void setMobSkin(Object entity, String skin) { @JSStaticFunction public static ItemInstance getDroppedItem(Object entity) { - return new ItemInstance(new NativeItemInstance(EntityMethod.getItemFromDrop(unwrapEntity(entity)))); + return ItemInstance.makeFrom(EntityMethod.getItemFromDrop(unwrapEntity(entity))); } @JSStaticFunction public static ItemInstance getProjectileItem(Object entity) { - return new ItemInstance(new NativeItemInstance(EntityMethod.getItemFromProjectile(unwrapEntity(entity)))); + return ItemInstance.makeFrom(EntityMethod.getItemFromProjectile(unwrapEntity(entity))); } @JSStaticFunction @Indev public static void setDroppedItem(Object entity, int id, int count, int data, Object extra) { - NativeAPI.setItemToDrop(unwrapEntity(entity), id, count, data, NativeItemInstanceExtra.unwrapObject(extra)); + NativeAPI.setItemToDrop(unwrapEntity(entity), id, count, data, ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction public static ItemInstance getCarriedItem(Object entity) { - return new ItemInstance(new NativeItemInstance(EntityMethod.getEntityCarriedItem(unwrapEntity(entity)))); + return ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(unwrapEntity(entity))); } @JSStaticFunction public static ItemInstance getOffhandItem(Object entity) { - return new ItemInstance(new NativeItemInstance(EntityMethod.getEntityOffhandItem(unwrapEntity(entity)))); + return ItemInstance.makeFrom(EntityMethod.getEntityOffhandItem(unwrapEntity(entity))); } @JSStaticFunction public static void setCarriedItem(Object entity, int id, int count, int data, Object extra) { NativeAPI.setEntityCarriedItem(unwrapEntity(entity), id, count, data, - NativeItemInstanceExtra.unwrapObject(extra)); + ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction public static void setOffhandItem(Object entity, int id, int count, int data, Object extra) { NativeAPI.setEntityOffhandItem(unwrapEntity(entity), id, count, data, - NativeItemInstanceExtra.unwrapObject(extra)); + ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction public static ItemInstance getArmor(Object entity, int slot) { - return new ItemInstance(new NativeItemInstance(EntityMethod.getEntityArmor(unwrapEntity(entity), slot))); + return ItemInstance.makeFrom(EntityMethod.getEntityArmor(unwrapEntity(entity), slot)); } @JSStaticFunction public static void setArmor(Object entity, int slot, int id, int count, int data, Object extra) { NativeAPI.setEntityArmor(unwrapEntity(entity), slot, id, count, data, - NativeItemInstanceExtra.unwrapObject(extra)); + ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction public static ItemInstance getArmorSlot(Object entity, int slot) { - return new ItemInstance(new NativeItemInstance(EntityMethod.getEntityArmor(unwrapEntity(entity), slot))); + return ItemInstance.makeFrom(EntityMethod.getEntityArmor(unwrapEntity(entity), slot)); } @JSStaticFunction public static void setArmorSlot(Object entity, int slot, int id, int count, int data, Object extra) { NativeAPI.setEntityArmor(unwrapEntity(entity), slot, id, count, data, - NativeItemInstanceExtra.unwrapObject(extra)); + ItemInstanceExtra.unwrapObject(extra)); } // other @@ -1084,8 +1098,8 @@ public static NativeAttributeInstance getAttribute(Object entity, String attribu } @JSStaticFunction - public static NativePathNavigation getPathNavigation(Object entity) { - return NativePathNavigation.getNavigation(unwrapEntity(entity)); + public static PathNavigation getPathNavigation(Object entity) { + return PathNavigation.getNavigation(unwrapEntity(entity)); } @JSStaticFunction @@ -1166,7 +1180,7 @@ public static void addItemCreativeInv(int id, int count, int data, Object extra) @JSStaticFunction @Deprecated public static ItemInstance getInventorySlot(int slot) { - return new ItemInstance(new NativeItemInstance(NativeAPI.getInventorySlot(slot))); + return new ItemInstance(); } @JSStaticFunction @@ -1178,37 +1192,37 @@ public static void setInventorySlot(int slot, int id, int count, int data, Objec @JSStaticFunction @Deprecated public static ItemInstance getArmorSlot(int slot) { - return new ItemInstance(new NativeItemInstance(EntityMethod.getEntityArmor(get(), slot))); + return ItemInstance.makeFrom(EntityMethod.getEntityArmor(get(), slot)); } @JSStaticFunction @Deprecated public static void setArmorSlot(int slot, int id, int count, int data, Object extra) { - EntityMethod.setEntityArmor(get(), slot, id, count, data, NativeItemInstanceExtra.unwrapObject(extra)); + EntityMethod.setEntityArmor(get(), slot, id, count, data, ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction @Deprecated public static ItemInstance getCarriedItem() { - return new ItemInstance(new NativeItemInstance(EntityMethod.getEntityCarriedItem(get()))); + return ItemInstance.makeFrom(EntityMethod.getEntityCarriedItem(get())); } @JSStaticFunction @Deprecated public static ItemInstance getOffhandItem() { - return new ItemInstance(new NativeItemInstance(EntityMethod.getEntityOffhandItem(get()))); + return ItemInstance.makeFrom(EntityMethod.getEntityOffhandItem(get())); } @JSStaticFunction @Deprecated public static void setCarriedItem(int id, int count, int data, Object extra) { - EntityMethod.setEntityCarriedItem(get(), id, count, data, NativeItemInstanceExtra.unwrapObject(extra)); + EntityMethod.setEntityCarriedItem(get(), id, count, data, ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction @Deprecated public static void setOffhandItem(int id, int count, int data, Object extra) { - EntityMethod.setEntityOffhandItem(get(), id, count, data, NativeItemInstanceExtra.unwrapObject(extra)); + EntityMethod.setEntityOffhandItem(get(), id, count, data, ItemInstanceExtra.unwrapObject(extra)); } @JSStaticFunction @@ -1619,65 +1633,6 @@ public static String getIdInfo(int id) { } } - @APIStaticModule - public static class Item extends NativeItem { - protected Item(int id, long ptr, String nameId, String nameToDisplay) { - super(id, CustomItem.getItemManager(id), nameId, nameToDisplay); - } - - @JSStaticFunction - public static NativeItem createFoodItem(int id, String nameId, String name, String iconName, int iconIndex, - int food) { - NativeItem item = NativeItem.createFoodItem(id, nameId, name, iconName, iconIndex, food); - item.setMaxUseDuration(32); - return item; - } - - @JSStaticFunction - public static String getName(int id, int data, Object extra) { - return getNameForId(id, data, NativeItemInstanceExtra.unwrapObject(extra)); - } - - @JSStaticFunction - public static int getMaxDamage(int id) { - return getMaxDamageForId(id, 0); - } - - @JSStaticFunction - public static int getMaxStackSize(int id, int data) { - return getMaxStackForId(id, data); - } - - @JSStaticFunction - @Deprecated(since = "Zote") - public static void setRequiresIconOverride(int id, boolean enabled) { - NativeItem.setItemRequiresIconOverride(id, enabled); - } - - @JSStaticFunction - @Deprecated(since = "Zote") - public static void overrideCurrentIcon(String name, int index) { - NativeItem.overrideItemIcon(name, index); - } - - @JSStaticFunction - @Deprecated(since = "Zote") - public static void overrideCurrentName(String name) { - NativeAPI.overrideItemName(name); - } - - @JSStaticFunction - public static void invokeItemUseOn(int id, int count, int data, Object extra, int x, int y, int z, int side, - double vx, double vy, double vz, Object entity) { - NativeAPI.invokeUseItemOn(id, count, data, NativeItemInstanceExtra.unwrapObject(extra), x, y, z, side, - (float) vx, (float) vy, (float) vz, Entity.unwrapEntity(entity)); - } - - public static void invokeItemUseNoTarget(int id, int count, int data, Object extra) { - NativeAPI.invokeUseItemNoTarget(id, count, data, NativeItemInstanceExtra.unwrapObject(extra)); - } - } - // keep old methods, but now inherit from apparatus api public static class Armor extends ActorArmorHandler { private Armor(EntityActor actor) { @@ -1696,12 +1651,12 @@ public static void preventDamaging(int id) { } } - public static class ItemExtraData extends NativeItemInstanceExtra { - public ItemExtraData(cn.nukkit.item.Item extra) { - super(extra != null ? constructClone(extra) : null); + public static class ItemExtraData extends ItemInstanceExtra { + public ItemExtraData(long extra) { + super(extra != 0 ? constructClone(extra) : 0); } - public ItemExtraData(NativeItemInstanceExtra extra) { + public ItemExtraData(ItemInstanceExtra extra) { super(extra); } @@ -2194,7 +2149,7 @@ public static int getSyncTime() { @APIStaticModule public static class Saver { @APIIgnore - public static interface IScopeSaver { + public interface IScopeSaver { void read(Object scope); Object save(); default Scriptable getDefaultSaves(){ @@ -2203,7 +2158,7 @@ default Scriptable getDefaultSaves(){ } @APIIgnore - public static interface IObjectSaver { + public interface IObjectSaver { Object read(ScriptableObject input); ScriptableObject save(Object input); Scriptable getDefaultSaves(); @@ -2234,9 +2189,9 @@ public Scriptable getDefaultSaves() { } @JSStaticFunction - public static int registerObjectSaver(final String name, Object scopeSaver) { + public static String registerObjectSaver(final String name, Object scopeSaver) { final IObjectSaver saver = (IObjectSaver) Context.jsToJava(scopeSaver, IObjectSaver.class); - return ObjectSaverRegistry.registerSaver(name, new ObjectSaver() { + ObjectSaverRegistry.registerSaver(name, new ObjectSaver() { @Override public Object read(ScriptableObject input) { return saver.read(input); @@ -2247,36 +2202,30 @@ public ScriptableObject save(Object input) { return saver.save(input); } }); + return name; } @JSStaticFunction public static String serializeToString(Object param) { - return ScriptableSerializer.jsonToString(ScriptableSerializer.scriptableToJson(param, null)); + SerializationContext ctx = new SerializationContext(); + return ScriptableSerializer.scriptableToJsonString(param, ctx); } @JSStaticFunction public static Object deserializeFromString(String param) { - try { - return ScriptableSerializer.scriptableFromJson(ScriptableSerializer.stringToJson(param)); - } catch (JSONException e) { - throw new RuntimeException(e.getMessage(), e); - } + SerializationContext ctx = new SerializationContext(); + return ScriptableSerializer.scriptableFromJsonString(param, ctx, false); } @JSStaticFunction - public static void registerObject(Object obj, int saverId) { + public static void registerObject(Object obj, String saverId) { ObjectSaverRegistry.registerObject(obj, saverId); } @JSStaticFunction public static void setObjectIgnored(ScriptableObject obj, boolean ignore) { - ObjectSaverRegistry.setObjectIgnored(obj, ignore); - } - - @JSStaticFunction - public static int getObjectSaverId(Object obj) { - ObjectSaver saver = ObjectSaverRegistry.getSaverFor(obj); - return saver != null ? saver.getSaverId() : -1; + // NOTE: This was not working for a long time already + logDeprecation("setObjectIgnored"); } } @@ -2779,37 +2728,6 @@ public SyncedNetworkData() { } } - public static class ItemContainer extends com.zhekasmirnov.apparatus.api.container.ItemContainer { - public ItemContainer() { - super(); - } - - @Deprecated(since = "Zote") - public ItemContainer(com.zhekasmirnov.innercore.api.mod.ui.container.Container container) { - super(container); - } - } - - public static class BlockSource extends NativeBlockSource { - public BlockSource(int dimension, boolean b1, boolean b2) { - super(dimension, b1, b2); - } - - public BlockSource(int dimension) { - super(dimension); - } - } - - public static class BlockState extends com.zhekasmirnov.apparatus.adapter.innercore.game.block.BlockState { - public BlockState(int id, int data) { - super(id, data); - } - - public BlockState(int id, Object scriptable) { - super(id, (Scriptable) scriptable); - } - } - public static class PlayerActor extends NativePlayer { public PlayerActor(long entity) { super(entity); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/PreferencesWindowAPI.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/PreferencesWindowAPI.java index 26f1761ff..0cf4324c2 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/PreferencesWindowAPI.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/adaptedscript/PreferencesWindowAPI.java @@ -11,6 +11,7 @@ import com.zhekasmirnov.innercore.mod.executable.Compiler; import com.zhekasmirnov.innercore.utils.FileTools; import com.zhekasmirnov.innercore.utils.IMessageReceiver; +import org.innercore.icstd.inventory.ItemContainer; import org.mozilla.javascript.Context; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.annotations.JSStaticFunction; @@ -178,16 +179,13 @@ public static String downloadFile(String sUrl, Object _handler) { } } - public static class WorkbenchRecipeListBuilder - extends com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchRecipeListBuilder { - public WorkbenchRecipeListBuilder(long player, - com.zhekasmirnov.apparatus.api.container.ItemContainer container) { + public static class WorkbenchRecipeListBuilder extends com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchRecipeListBuilder { + public WorkbenchRecipeListBuilder(long player, ItemContainer container) { super(player, container); } } - public static class WorkbenchRecipeListProcessor - extends com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchRecipeListProcessor { + public static class WorkbenchRecipeListProcessor extends com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchRecipeListProcessor { public WorkbenchRecipeListProcessor(ScriptableObject target) { super(target); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/RecipeRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/RecipeRegistry.java index d0c5e3e0e..8752a26f7 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/RecipeRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/RecipeRegistry.java @@ -1,8 +1,8 @@ package com.zhekasmirnov.innercore.api.mod.recipes; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import com.zhekasmirnov.innercore.api.mod.recipes.furnace.FurnaceRecipe; @@ -22,11 +22,11 @@ public class RecipeRegistry { static final String LOGGER_TAG = "INNERCORE-RECIPES"; - private static String tryGetReadableStringFromResult(ItemInstance result) { - int id = result.getId(); + private static String tryGetReadableStringFromResult(org.innercore.icstd.inventory.ItemInstance result) { + int id = result.id; String name = IDRegistry.getNameByID(id); name = name != null ? name : Integer.toString(id); - return result.getCount() + "x " + name + "#" + result.getData() + (result.getExtraValue() != null ? "[+extra]" : ""); + return result.count + "x " + name + "#" + result.data + (result.extra != null ? "[+extra]" : ""); } private static void reportComponentArrayError(Object[] components, String errorPrefix) { @@ -122,14 +122,16 @@ private static HashMap extractShapelessEntries(NativeArr } @JSStaticFunction - public static WorkbenchShapedRecipe addShapedGeneric(ScriptableObject result, NativeArray mask, NativeArray data, Function func, String prefix, boolean vanilla) { - int id = ScriptableObjectHelper.getIntProperty(result, "id", 0); + public static WorkbenchShapedRecipe addShapedGeneric(Scriptable result, NativeArray mask, NativeArray data, Function func, String prefix, boolean vanilla) { + Object rawId = ScriptableObjectHelper.getProperty(result, "id", null); + int id; + if (!(rawId instanceof Number) || (id = ((Number) rawId).intValue()) == 0) { + ICLog.i("ERROR", "shaped recipe id is invalid: " + rawId); + return null; + } int count = ScriptableObjectHelper.getIntProperty(result, "count", 1); int aux = ScriptableObjectHelper.getIntProperty(result, "data", 0); - NativeItemInstanceExtra extra = NativeItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(result, "extra", 0)); - if (id == 0) { - throw new IllegalArgumentException("recipe id is 0"); - } + ItemInstanceExtra extra = ItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(result, "extra", 0)); WorkbenchShapedRecipe recipe = new WorkbenchShapedRecipe(id, count, aux, extra); recipe.setCallback(func); @@ -170,14 +172,16 @@ public static WorkbenchShapedRecipe addShaped2(int id, int count, int aux, Nativ } @JSStaticFunction - public static WorkbenchShapelessRecipe addShapelessGeneric(ScriptableObject result, NativeArray data, Function func, String prefix, boolean vanilla) { - int id = ScriptableObjectHelper.getIntProperty(result, "id", 0); + public static WorkbenchShapelessRecipe addShapelessGeneric(Scriptable result, NativeArray data, Function func, String prefix, boolean vanilla) { + Object rawId = ScriptableObjectHelper.getProperty(result, "id", null); + int id; + if (!(rawId instanceof Number) || (id = ((Number) rawId).intValue()) == 0) { + ICLog.i("ERROR", "shapeless recipe id is invalid: " + rawId); + return null; + } int count = ScriptableObjectHelper.getIntProperty(result, "count", 1); int aux = ScriptableObjectHelper.getIntProperty(result, "data", 0); - NativeItemInstanceExtra extra = NativeItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(result, "extra", 0)); - if (id == 0) { - throw new IllegalArgumentException("recipe id is 0"); - } + ItemInstanceExtra extra = ItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(result, "extra", 0)); WorkbenchShapelessRecipe recipe = new WorkbenchShapelessRecipe(id, count, aux, extra); recipe.setCallback(func); @@ -334,7 +338,7 @@ public static void removeFurnaceFuel(int id, int data) { } @JSStaticFunction - public static ScriptableObject getFurnaceRecipeResult(int id, int data, String prefix) { + public static Scriptable getFurnaceRecipeResult(int id, int data, String prefix) { FurnaceRecipe recipe = FurnaceRecipeRegistry.getRecipe(id, data, prefix); return recipe != null ? recipe.getResult() : null; } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipe.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipe.java index 2ecaa47c6..4ee669659 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipe.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipe.java @@ -3,8 +3,8 @@ import android.util.Pair; import com.zhekasmirnov.apparatus.minecraft.addon.recipe.AddonRecipeParser; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; import com.zhekasmirnov.innercore.api.mod.recipes.workbench.RecipeEntry; +import org.innercore.icstd.inventory.ItemInstance; import org.json.JSONObject; /** diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipeRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipeRegistry.java index 144c6b4eb..be7f8fe5b 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipeRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/furnace/FurnaceRecipeRegistry.java @@ -1,10 +1,10 @@ package com.zhekasmirnov.innercore.api.mod.recipes.furnace; import com.zhekasmirnov.innercore.api.NativeFurnaceRegistry; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.recipes.workbench.RecipeEntry; import com.zhekasmirnov.innercore.utils.FileTools; +import org.innercore.icstd.inventory.ItemInstance; import org.json.JSONArray; import org.json.JSONObject; @@ -114,7 +114,7 @@ public static Collection getFurnaceRecipeByResult(int id, int aux for (FurnaceRecipe recipe : allRecipes) { ItemInstance result = recipe.getResult(); - if (result.getId() == id && (aux == -1 || result.getData() == aux) || recipe.isMatchingPrefix(prefix)) { + if (result.id == id && (aux == -1 || result.data == aux) || recipe.isMatchingPrefix(prefix)) { found.add(recipe); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/InventoryPool.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/InventoryPool.java index 8ac67cf33..b50ca73b4 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/InventoryPool.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/InventoryPool.java @@ -1,10 +1,12 @@ package com.zhekasmirnov.innercore.api.mod.recipes.workbench; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; import com.zhekasmirnov.apparatus.mcpe.NativePlayer; import com.zhekasmirnov.innercore.api.NativeItem; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; +import org.innercore.icstd.modules.ItemModule; + import java.util.ArrayList; import java.util.HashMap; @@ -17,7 +19,7 @@ public class InventoryPool { public class PoolEntry { public int slot, id, count, data; - public NativeItemInstanceExtra extra; + public ItemInstanceExtra extra; public PoolEntry(int slot, int id, int count, int data) { this.slot = slot; @@ -27,7 +29,7 @@ public PoolEntry(int slot, int id, int count, int data) { } public PoolEntry(int slot) { - ItemStack item = player.getInventorySlot(slot); + ItemInstance item = player.getInventorySlot(slot); this.slot = slot; this.id = item.id; this.count = item.count; @@ -200,7 +202,7 @@ else if (!first.isMatches(entry)) { continue; } - if (NativeItem.getMaxStackForId(first.id, first.data) == 1) { + if (ItemModule.getMaxStack(first.id, first.data) == 1) { amountToGet = 1; } int got = entry.getAmountOfItem(amountToGet); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipe.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipe.java index 9573734ae..8dd57be51 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipe.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipe.java @@ -4,9 +4,8 @@ import com.zhekasmirnov.apparatus.minecraft.addon.recipe.AddonRecipeParser; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeAPI; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; import com.zhekasmirnov.innercore.api.runtime.Callback; import com.zhekasmirnov.innercore.mod.executable.Compiler; @@ -30,14 +29,14 @@ private static synchronized long nextRecipeUid() { protected int id, count, data; - protected NativeItemInstanceExtra extra; + protected ItemInstanceExtra extra; protected HashMap entries = new HashMap<>(); boolean isValid = true; boolean isVanilla = false; private final long recipeUid = nextRecipeUid(); - public WorkbenchRecipe(int id, int count, int data, NativeItemInstanceExtra extra) { + public WorkbenchRecipe(int id, int count, int data, ItemInstanceExtra extra) { this.id = id; this.count = count; this.data = data; diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListBuilder.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListBuilder.java index 77e5c65c1..3801a31b1 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListBuilder.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListBuilder.java @@ -1,8 +1,8 @@ package com.zhekasmirnov.innercore.api.mod.recipes.workbench; import android.util.Pair; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.apparatus.api.container.ItemContainer; +import org.innercore.icstd.inventory.ItemContainer; +import org.innercore.icstd.inventory.ItemInstance; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -53,7 +53,7 @@ public JSONObject buildAvailableRecipesPacket(RecipeComparator comparator) { packet.put("recipes", jsonList); for (Pair pair : recipeVisibility) { - ItemStack result = ItemStack.parse(pair.first.getResult()); + ItemInstance result = ItemInstance.parse(pair.first.getResult(), false); JSONObject recipeJson = new JSONObject(); recipeJson.put("id", pair.first.getRecipeUid()); recipeJson.put("result", result.asJson()); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListProcessor.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListProcessor.java index e7f8fed82..0b1d199ea 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListProcessor.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeListProcessor.java @@ -1,10 +1,10 @@ package com.zhekasmirnov.innercore.api.mod.recipes.workbench; -import com.zhekasmirnov.apparatus.adapter.innercore.game.item.ItemStack; -import com.zhekasmirnov.apparatus.api.container.ItemContainer; import com.zhekasmirnov.apparatus.multiplayer.mod.IdConversionMap; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import com.zhekasmirnov.innercore.api.mod.util.ScriptableFunctionImpl; +import org.innercore.icstd.inventory.ItemContainer; +import org.innercore.icstd.inventory.ItemInstance; import org.json.JSONArray; import org.json.JSONObject; import org.mozilla.javascript.Context; @@ -133,7 +133,7 @@ public void setListener(OnRecipeSelectRequestedListener listener) { this.listener = listener; } - public int processRecipeListPacket(ItemContainer container, JSONObject packet) { + public int processRecipeListPacket(org.innercore.icstd.inventory.ItemContainer container, JSONObject packet) { if (container.isServer) { throw new IllegalArgumentException("requires client container"); } @@ -147,7 +147,7 @@ public int processRecipeListPacket(ItemContainer container, JSONObject packet) { long uid = recipe.optLong("id"); JSONObject resultJson = recipe.optJSONObject("result"); if (resultJson != null && uid != 0) { - ItemStack result = ItemStack.parse(resultJson); + ItemInstance result = ItemInstance.parse(resultJson, true); if (result != null) { result.id = IdConversionMap.serverToLocal(result.id); container.setSlot(PREFIX + index, result.id, result.count, result.data, result.extra); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeRegistry.java index 0d35bd2eb..5b38883f3 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchRecipeRegistry.java @@ -6,9 +6,9 @@ import com.zhekasmirnov.apparatus.mcpe.NativePlayer; import com.zhekasmirnov.apparatus.mcpe.NativeWorkbench; import com.zhekasmirnov.innercore.api.NativeAPI; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; +import org.innercore.icstd.inventory.ItemInstance; import java.util.*; @@ -281,7 +281,7 @@ public static UIRecipeLists getAvailableRecipesForPlayerInventory(long playerUid NativePlayer player = new NativePlayer(playerUid); for (int i = 0; i < 36; i++) { - ItemStack item = player.getInventorySlot(i); + ItemInstance item = player.getInventorySlot(i); if (item.id != 0 && item.count > 0) { addItemToInvMap(inventory, item.id, item.count, item.data); addRecipesThatContainItem(item.id, item.data, visibleRecipes); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapedRecipe.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapedRecipe.java index 942429940..a38d8605f 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapedRecipe.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapedRecipe.java @@ -5,7 +5,7 @@ import com.zhekasmirnov.apparatus.mcpe.NativeWorkbench; import com.zhekasmirnov.apparatus.minecraft.addon.recipe.AddonRecipeParser; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; import org.json.JSONArray; @@ -21,7 +21,7 @@ public class WorkbenchShapedRecipe extends WorkbenchRecipe { private RecipeEntry[][] pattern; - public WorkbenchShapedRecipe(int id, int count, int data, NativeItemInstanceExtra extra) { + public WorkbenchShapedRecipe(int id, int count, int data, ItemInstanceExtra extra) { super(id, count, data, extra); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapelessRecipe.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapelessRecipe.java index dcb656fc0..d555ae061 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapelessRecipe.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/recipes/workbench/WorkbenchShapelessRecipe.java @@ -5,7 +5,7 @@ import com.zhekasmirnov.apparatus.mcpe.NativeWorkbench; import com.zhekasmirnov.apparatus.minecraft.addon.recipe.AddonRecipeParser; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import org.json.JSONArray; import org.json.JSONObject; @@ -19,7 +19,7 @@ */ public class WorkbenchShapelessRecipe extends WorkbenchRecipe { - public WorkbenchShapelessRecipe(int id, int count, int data, NativeItemInstanceExtra extra) { + public WorkbenchShapelessRecipe(int id, int count, int data, ItemInstanceExtra extra) { super(id, count, data, extra); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/AbstractSlot.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/AbstractSlot.java index 95b3425dc..2d67244ff 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/AbstractSlot.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/AbstractSlot.java @@ -1,6 +1,6 @@ package com.zhekasmirnov.innercore.api.mod.ui.container; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; public interface AbstractSlot { int getId(); @@ -9,9 +9,9 @@ public interface AbstractSlot { int getData(); - NativeItemInstanceExtra getExtra(); + ItemInstanceExtra getExtra(); - void set(int id, int count, int data, NativeItemInstanceExtra extra); + void set(int id, int count, int data, ItemInstanceExtra extra); void validate(); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Container.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Container.java index 7b8bcdb34..5b4cbe439 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Container.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Container.java @@ -1,14 +1,22 @@ package com.zhekasmirnov.innercore.api.mod.ui.container; -import com.reider745.InnerCoreServer; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import com.zhekasmirnov.innercore.api.NativeAPI; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.log.DialogHelper; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectWrapper; import com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchField; import com.zhekasmirnov.innercore.api.mod.ui.elements.UIElement; +import com.zhekasmirnov.innercore.api.mod.ui.elements.UISlotElement; import com.zhekasmirnov.innercore.api.mod.ui.window.IWindow; import com.zhekasmirnov.innercore.api.runtime.Callback; +import com.zhekasmirnov.innercore.api.runtime.MainThreadQueue; import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaver; import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; + +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.modules.ItemModule; +import org.innercore.icstd.serialization.*; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; @@ -20,28 +28,95 @@ * Created by zheka on 02.08.2017. */ -@Deprecated(since = "Zote") -public class Container implements WorkbenchField, UiAbstractContainer, ObjectSaverRegistry.IHashSaver { +public class Container implements WorkbenchField, UiAbstractContainer, ISerializableObject { public static final boolean isContainer = true; - private static final int saverId; - static { - saverId = ObjectSaverRegistry.registerSaver("_container", new ObjectSaver() { - @Override - public Object read(ScriptableObject input) { - Container con = new Container(); - con.slots = input; - return con; + private static final String saverId = ObjectSerializer.registerType("ic:old_container", Container.class, new IObjectSerializer() { + @Override + public Container read(IScopedReader reader, SerializationContext ctx, String typeId) { + Container container = new Container(); + IObjectReader slots = reader.asObject(); + if (slots == null) { + ctx.addError("container serializer expected a slot map"); + reader.endScope(); + return null; } + for (String name : slots.iterKeys()) { + IArrayReader slot = slots.beginScope(name).asArray(); + if (slot != null && slot.size() > 2) { + ScriptableObject s = (ScriptableObject) container.getSlot(name); + s.put("id", s, slot.getInt(0, 0)); + s.put("count", s, slot.getInt(1, 0)); + s.put("data", s, slot.getInt(2, 0)); + if (slot.size() > 3) { + ItemInstanceExtra extra = ObjectSerializer.deserializeObject(slot.beginScope(3), ItemInstanceExtra.class, ctx, true); + if (extra != null) { + s.put("extra", s, extra); + } + } + } else if (slot != null && slot.size() == 0) { + container.getSlot(name); + } else { + ctx.addError("invalid container slot format " + name); + } + slots.endChildScope(); + } + reader.endScope(); + return container; + } - @Override - public ScriptableObject save(Object input) { - if (input instanceof Container) { - return ((Container) input).slots; + @Override + public void write(IScopedWriter writer, Container container, SerializationContext ctx, String typeId) { + if (container == null) throw new IllegalArgumentException("container is null"); + IObjectWriter slots = writer.asObject(); + for (Object key : container.slots.getIds()) { + if (!(key instanceof String)) continue; + Object s_ = container.getSlot((String) key); + if (!(s_ instanceof ScriptableObject)) continue; + + ScriptableObject s = (ScriptableObject) s_; + int id = ScriptableObjectHelper.getIntProperty(s, "id", 0); + int count = ScriptableObjectHelper.getIntProperty(s, "count", 0); + int data = ScriptableObjectHelper.getIntProperty(s, "data", 0); + Object extra = ScriptableObjectHelper.getProperty(s, "extra", null); + + IArrayWriter slot = slots.beginScope((String) key).asArray(); + if (id == 0 && count == 0 && data == 0) { + slot.endScope(); // empty slot + continue; } - return null; + slot.addInt(id); + slot.addInt(count); + slot.addInt(data); + if (extra instanceof ItemInstanceExtra) { + ObjectSerializer.serializeObject(slot.beginScope(), extra, ctx, true); + } + slot.endScope(); } - }); + writer.endScope(); + } + }); + + private static final String legacySaverId = ObjectSaverRegistry.registerSaverStr("_container", new ObjectSaver() { + @Override + public Object read(ScriptableObject input) { + Container con = new Container(); + con.slots = input; + return con; + } + + @Override + public ScriptableObject save(Object input) { + if (input instanceof Container) { + return ((Container) input).slots; + } + return null; + } + }); + + @Override + public String getSerializationTypeId() { + return saverId; } public static void initSaverId() { @@ -49,7 +124,6 @@ public static void initSaverId() { } public Container() { - ObjectSaverRegistry.registerObject(this, saverId); } public Object parent, tileEntity; @@ -74,6 +148,7 @@ public String getClassName() { } }; + public Object getSlot(String name) { if (slots.has(name, slots)) { return slots.get(name); @@ -88,7 +163,8 @@ public Slot getFullSlot(String name) { Object _slot = slots.get(name); if (_slot instanceof Slot) { return (Slot) _slot; - } else if (_slot instanceof ScriptableObject) { + } + else if (_slot instanceof ScriptableObject) { return new Slot((ScriptableObject) _slot); } } @@ -104,7 +180,36 @@ public UiVisualSlotImpl getSlotVisualImpl(String name) { @Override public void handleInventoryToSlotTransaction(int inventorySlotId, String slotName, int amount0) { - InnerCoreServer.useClientMethod("Container.handleInventoryToSlotTransaction(inventorySlotId, slotName, amount0)"); + MainThreadQueue.serverThread.enqueue(() -> { + int amount = amount0; + UIElement element = getElement(slotName); + UISlotElement slotElement = element instanceof UISlotElement ? (UISlotElement) element : null; + if (amount > 0 && (slotElement == null || !slotElement.isVisual)) { + Object slotObj = getSlot(slotName); + if (slotObj instanceof ScriptableObject) { + ItemInstance invSlot = ItemInstance.makeFromPointer(NativeAPI.getInventorySlot(inventorySlotId)); + ScriptableObjectWrapper slot = new ScriptableObjectWrapper((ScriptableObject) slotObj); + if (slot.getInt("id") == 0 || slot.getInt("id") == invSlot.id && slot.getInt("data") == invSlot.data && slot.get("extra") == null) { + if (amount > invSlot.count) { + amount = invSlot.count; + } + if (slotElement == null || slotElement.isValidItem(invSlot.id, invSlot.count, invSlot.data, invSlot.extra)) { + int maxStackSize = slotElement != null && slotElement.maxStackSize > 0 ? slotElement.maxStackSize : ItemModule.getMaxStack(invSlot.id, invSlot.data); + amount = Math.min(amount, Math.min(invSlot.count, maxStackSize - slot.getInt("count"))); + slot.put("count", slot.getInt("count") + amount); + slot.put("id", invSlot.id); + slot.put("data", invSlot.data); + slot.put("extra", ItemInstanceExtra.cloneExtra(invSlot.extra)); + if (amount < invSlot.count) { + NativeAPI.setInventorySlot(inventorySlotId, invSlot.id, invSlot.count - amount, invSlot.data, ItemInstanceExtra.getValueOrNullPtr(invSlot.extra)); + } else { + NativeAPI.setInventorySlot(inventorySlotId, 0, 0, 0, 0); + } + } + } + } + } + }); } @Override @@ -114,14 +219,41 @@ public void handleSlotToSlotTransaction(String from, String to, int amount) { @Override public void handleSlotToInventoryTransaction(String slotName, int amount0) { - InnerCoreServer.useClientMethod("Container.handleSlotToInventoryTransaction(slotName, amount0)"); + MainThreadQueue.serverThread.enqueue(() -> { + int amount = amount0; + UIElement element = getElement(slotName); + UISlotElement slotElement = element instanceof UISlotElement ? (UISlotElement) element : null; + if (amount > 0 && (slotElement == null || !slotElement.isVisual)) { + Object slotObj = getSlot(slotName); + if (slotObj instanceof ScriptableObject) { + ScriptableObjectWrapper slot = new ScriptableObjectWrapper((ScriptableObject) slotObj); + int id = slot.getInt("id"); + int count = slot.getInt("count"); + int data = slot.getInt("data"); + ItemInstanceExtra extra = ItemInstanceExtra.unwrapObject(slot.get("extra")); + if (id != 0 && count > 0) { + amount = Math.min(amount, count); + count -= amount; + NativeAPI.addItemToInventory(id, amount, data, ItemInstanceExtra.getValueOrNullPtr(extra), true); + if (count <= 0) { + id = data = count = 0; + extra = null; + } + slot.put("id", id); + slot.put("count", count); + slot.put("data", data); + slot.put("extra", extra); + } + } + } + }); } public void setSlot(String name, int id, int count, int data) { getFullSlot(name).set(id, count, data); } - public void setSlot(String name, int id, int count, int data, NativeItemInstanceExtra extra) { + public void setSlot(String name, int id, int count, int data, ItemInstanceExtra extra) { getFullSlot(name).set(id, count, data, extra); } @@ -155,6 +287,8 @@ public void validateAll() { } } + + private boolean isOpened = false; private IWindow window; private HashMap elements = new HashMap<>(); @@ -208,18 +342,6 @@ public void close() { } } - private int id; - - @Override - public int getId() { - return id; - } - - @Override - public void setId(int id) { - this.id = id; - } - public interface OnCloseListener { void onClose(Container c, IWindow win); } @@ -271,6 +393,7 @@ public void onWindowClosed() { } } + public boolean isOpened() { return this.window != null && this.window.isOpened(); } @@ -334,6 +457,8 @@ private void setupInitialBindings() { } } + + public void setScale(String name, float value) { setBinding(name, "value", value); } @@ -355,6 +480,7 @@ public boolean isElementTouched(String name) { return element != null && element.isTouched; } + public void invalidateUIElements(boolean onCurrentThread) { if (window != null) { window.invalidateElements(onCurrentThread); @@ -386,11 +512,11 @@ public void invalidateUI() { invalidateUI(false); } - public void refreshSlots() { - } - public void applyChanges() { - } + public void refreshSlots() {} + + public void applyChanges() {} + private String wbSlotNamePrefix = "slot"; diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/ScriptableUiVisualSlotImpl.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/ScriptableUiVisualSlotImpl.java index 607b1ed11..87e547a8c 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/ScriptableUiVisualSlotImpl.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/ScriptableUiVisualSlotImpl.java @@ -1,6 +1,6 @@ package com.zhekasmirnov.innercore.api.mod.ui.container; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import org.mozilla.javascript.ScriptableObject; @@ -28,7 +28,7 @@ public int getData() { } @Override - public NativeItemInstanceExtra getExtra() { - return NativeItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(scriptable, "extra", null)); + public ItemInstanceExtra getExtra() { + return ItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(scriptable, "extra", null)); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Slot.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Slot.java index 5621351d4..0e367c1e3 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Slot.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/Slot.java @@ -1,11 +1,8 @@ package com.zhekasmirnov.innercore.api.mod.ui.container; import com.zhekasmirnov.innercore.api.NativeAPI; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; - -import cn.nukkit.item.Item; - import org.mozilla.javascript.ScriptableObject; /** @@ -25,7 +22,7 @@ public Slot(int id, int count, int data) { set(id, count, data); } - public Slot(int id, int count, int data, NativeItemInstanceExtra extra) { + public Slot(int id, int count, int data, ItemInstanceExtra extra) { target = this; set(id, count, data, extra); } @@ -46,7 +43,7 @@ public void set(int id, int count, int data) { put("extra", null); } - public void set(int id, int count, int data, NativeItemInstanceExtra extra) { + public void set(int id, int count, int data, ItemInstanceExtra extra) { set(id, count, data); put("extra", extra); } @@ -68,7 +65,6 @@ public void validate() { } } - @Deprecated(since = "Zote") public void drop(float x, float y, float z) { int id = getInt("id"); int count = getInt("count"); @@ -94,12 +90,12 @@ public int getData() { return getInt("data"); } - public Item getExtraValue() { - return NativeItemInstanceExtra.unwrapValue(ScriptableObjectHelper.getProperty(target, "extra", null)); + public long getExtraValue() { + return ItemInstanceExtra.unwrapValue(ScriptableObjectHelper.getProperty(target, "extra", null)); } - public NativeItemInstanceExtra getExtra() { - return NativeItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(target, "extra", null)); + public ItemInstanceExtra getExtra() { + return ItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(target, "extra", null)); } public ScriptableObject save() { diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/UiVisualSlotImpl.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/UiVisualSlotImpl.java index 4827de865..e06bf81d4 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/UiVisualSlotImpl.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/container/UiVisualSlotImpl.java @@ -1,6 +1,6 @@ package com.zhekasmirnov.innercore.api.mod.ui.container; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; @Deprecated(since = "Zote") public interface UiVisualSlotImpl { @@ -10,5 +10,5 @@ public interface UiVisualSlotImpl { int getData(); - NativeItemInstanceExtra getExtra(); + ItemInstanceExtra getExtra(); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/elements/UISlotElement.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/elements/UISlotElement.java index f97a13c4f..4844b171f 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/elements/UISlotElement.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/ui/elements/UISlotElement.java @@ -2,7 +2,7 @@ import android.graphics.Bitmap; import android.graphics.Canvas; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import com.zhekasmirnov.innercore.api.mod.ui.container.UiAbstractContainer; import com.zhekasmirnov.innercore.api.mod.ui.container.UiVisualSlotImpl; import com.zhekasmirnov.innercore.api.mod.ui.types.Texture; @@ -31,7 +31,7 @@ public UISlotElement(UIWindow window, ScriptableObject description) { public boolean disablePixelPerfect = false; public int curId = 0, curCount = 0, curData = 0; - public NativeItemInstanceExtra curExtra; + public ItemInstanceExtra curExtra; protected boolean isSelectionForced = false; protected boolean wasSelected = false, isSelected = false; @@ -60,7 +60,7 @@ public int getMaxStackSize() { return 64; } - public boolean isValidItem(int id, int count, int data, NativeItemInstanceExtra extra) { + public boolean isValidItem(int id, int count, int data, ItemInstanceExtra extra) { return false; } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/mod/util/InventorySource.java b/src/main/java/com/zhekasmirnov/innercore/api/mod/util/InventorySource.java index ab87307e5..91569d728 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/mod/util/InventorySource.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/mod/util/InventorySource.java @@ -1,6 +1,6 @@ package com.zhekasmirnov.innercore.api.mod.util; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +import org.innercore.icstd.inventory.ItemInstanceExtra; import org.mozilla.javascript.ScriptableObject; /** @@ -37,7 +37,7 @@ public String getClassName() { return slots[slotId]; } - public static void setSource(int slotId, int id, int count, int data, NativeItemInstanceExtra extra) { + public static void setSource(int slotId, int id, int count, int data, ItemInstanceExtra extra) { ScriptableObject slot = getSource(slotId); slot.put("id", slot, id); slot.put("count", slot, count); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/particles/ParticleRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/particles/ParticleRegistry.java index 895c10fea..34b44e920 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/particles/ParticleRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/particles/ParticleRegistry.java @@ -2,8 +2,8 @@ import com.reider745.InnerCoreServer; import com.zhekasmirnov.innercore.api.NativeAPI; -import com.zhekasmirnov.innercore.api.commontypes.Coords; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectWrapper; +import org.innercore.icstd.common.script.GenericScriptPos3; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.annotations.JSStaticFunction; @@ -276,8 +276,8 @@ public void stop() { public void release() { } - public Coords getPosition() { - return new Coords(0, 0, 0); + public GenericScriptPos3 getPosition() { + return new GenericScriptPos3(0, 0, 0); } public float[] getPositionArray() { diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/Callback.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/Callback.java index 9e95e934b..9755f0715 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/Callback.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/Callback.java @@ -1,122 +1,33 @@ package com.zhekasmirnov.innercore.api.runtime; -import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.innercore.api.log.DialogHelper; import com.zhekasmirnov.innercore.api.log.ICLog; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.innercore.icstd.event.EventController; +import org.innercore.icstd.event.Events; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; import org.mozilla.javascript.Scriptable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.atomic.AtomicLong; - /** * Created by zheka on 09.08.2017. */ public class Callback { - private static class CallbackFunction { - public final Function function; - public final int priority; - - public CallbackFunction(Function function, int priority) { - this.function = function; - this.priority = priority; - } - } - - private static HashMap> callbacks = new HashMap<>(); - public static boolean profilingEnabled = false, profilingShowParameters = false; - - public static int count(String name) { - List encounted = callbacks.getOrDefault(name, null); - return encounted != null ? encounted.size() : 0; - } - public static void addCallback(String name, Function func, int priority) { - if (!callbacks.containsKey(name)) { - callbacks.put(name, new ArrayList()); - } - - CallbackFunction callback = new CallbackFunction(func, priority); - ArrayList callbacksByName = callbacks.get(name); - for (int i = 0; i < callbacksByName.size(); i++) { - if (callbacksByName.get(i).priority < priority) { - callbacksByName.add(i, callback); - return; - } - } - callbacksByName.add(callback); + Events.onScriptEvent(name, (ctrl, args) -> { + Scriptable scope = func.getParentScope(); + if (ctrl.scriptCtx == null) + ctrl.scriptCtx = Compiler.assureContextForCurrentThread(); + func.call((Context) ctrl.scriptCtx, scope, scope, args); + }).setPriority(priority); } public static void invokeCallbackV(String name, Object[] params) { - Context ctx = Compiler.assureContextForCurrentThread(); - boolean isTick = name.contains("tick") || name.contains("Tick"); - if (profilingShowParameters && !isTick) { - try { - Logger.debug("Profiling/" + name, - ScriptableObjectHelper.stringify(ScriptableObjectHelper.createArray(params), null)); - } catch (RuntimeException e) { - } - } - ArrayList funcs = callbacks.get(name); - Scriptable parent; - if (funcs != null) { - long profilingNanos = !profilingEnabled || isTick ? 0 : System.nanoTime(); - synchronized (funcs) { - for (CallbackFunction callback : funcs) { - parent = callback.function.getParentScope(); - callback.function.call(ctx, parent, parent, params); - } - } - if (profilingNanos != 0) { - profilingNanos = System.nanoTime() - profilingNanos; - Logger.debug("Profiling/" + name, - funcs.size() + " functions in " + (profilingNanos / 1000000f) + "ms."); - } - } - } - - public static List getCallbackAsRunnableList(String name, final Object[] params) { - List result = new ArrayList(); - boolean isTick = name.contains("tick") || name.contains("Tick"); - if (profilingShowParameters && !isTick) { - result.add(() -> { - try { - Logger.debug("Profiling/" + name, - ScriptableObjectHelper.stringify(ScriptableObjectHelper.createArray(params), null)); - } catch (RuntimeException e) { - } - }); - } - ArrayList funcs = callbacks.get(name); - if (funcs != null) { - AtomicLong profilingNanos = new AtomicLong(); - if (profilingEnabled && !isTick) { - result.add(() -> profilingNanos.set(System.nanoTime())); - } - synchronized (funcs) { - for (CallbackFunction func0 : funcs) { - result.add(new Runnable() { - public void run() { - Scriptable parent = func0.function.getParentScope(); - func0.function.call(Compiler.assureContextForCurrentThread(), parent, parent, params); - } - }); - } - } - if (profilingNanos.get() != 0) { - result.add(() -> Logger.debug("Profiling/" + name, funcs.size() + " functions in " - + ((System.nanoTime() - profilingNanos.get()) / 1000000f) + "ms.")); - } - } - return result; + Events.invokeScriptEvent(null, name, params); } - public static void invokeCallback(String name, Object... params) { + public static void invokeCallback(String name, Object ... params) { invokeCallbackV(name, params); } @@ -124,13 +35,16 @@ public static void invokeAPICallbackUnsafe(String name, Object[] params) { invokeCallbackV(name, params); } - public static Throwable invokeAPICallback(String name, Object... params) { - try { - invokeAPICallbackUnsafe(name, params); - } catch (Throwable e) { - ICLog.e("INNERCORE-CALLBACK", "error occurred while calling callback " + name, e); - return e; + public static Throwable invokeAPICallback(String name, Object ... params) { + EventController ctrl = new EventController(); + ctrl.catchExceptions = true; + Events.invokeScriptEvent(ctrl, name, params); + if (ctrl.errors != null && !ctrl.errors.isEmpty()) { + for (Throwable e : ctrl.errors) { + ICLog.e("INNERCORE-CALLBACK", "error occurred while calling callback " + name, e); + } + DialogHelper.reportNonFatalErrorList("Non-Fatal error occurred in callback " + name, ctrl.errors); } - return null; + return null; // compatibility for potential legacy java mods, result is unused } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/TickManager.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/TickManager.java deleted file mode 100644 index 51b60fe90..000000000 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/TickManager.java +++ /dev/null @@ -1,138 +0,0 @@ -package com.zhekasmirnov.innercore.api.runtime; - -import com.zhekasmirnov.innercore.api.log.ICLog; -import com.zhekasmirnov.innercore.api.runtime.saver.world.WorldDataSaverHandler; - -// WARNING! this class is no longer used and kept here like a stub, in case some mods need it -public class TickManager { - private static TickManager currentThread; - private static boolean isCurrentTreadStopped = false; - - private boolean running = false; - private boolean pause = false; - - public static int globalTickCounter = 0; // reserve time in case thread has stopped - public int time = 0; - - private static void reportFatalError(Throwable e) { - ICLog.e("THREADING", "error occurred in ticking thread, it will be stopped for current session", e); - } - - public static void nativeTick() { - if (currentThread == null) { - return; - } - - if (currentThread.running && !currentThread.pause) { - try { - currentThread.callTick(Tick.next()); - } catch (Throwable e) { - reportFatalError(e); - stop(); - } - } - } - - public void prepare() { - Tick.resetTickCounter(); - } - - public static void stop() { - if (currentThread != null) { - currentThread.running = false; - currentThread = null; - isCurrentTreadStopped = true; - } - } - - public static void setupAndStart() { - stop(); - - currentThread = new TickManager(); - currentThread.prepare(); - globalTickCounter = 0; - - currentThread.running = true; - currentThread.pause = false; - isCurrentTreadStopped = false; - - ICLog.d("THREADING", "ticking thread started"); - } - - public static boolean isStopped() { - return isCurrentTreadStopped; - } - - public static Throwable getLastFatalError() { - return null; - } - - public static void clearLastFatalError() { - } - - public static int getTime() { - return currentThread != null ? currentThread.time : globalTickCounter; - } - - public static boolean isPaused() { - return currentThread != null && currentThread.pause; - } - - public static boolean isRunningNow() { - return false; - } - - public static void setPaused(boolean paused) { - if (currentThread != null) { - currentThread.pause = paused; - } - } - - public static void resume() { - setPaused(false); - } - - public static void pause() { - setPaused(true); - } - - private static final Object[] EMPTY_ARGS = new Object[] {}; - - private void callTick(Tick tick) { - if (tick == null) { - time++; - } else { - time = tick.time; - } - globalTickCounter++; - - WorldDataSaverHandler.getInstance().onTick(); - - TickExecutor executor = TickExecutor.getInstance(); - if (executor.isAvailable()) { - executor.execute(Callback.getCallbackAsRunnableList("tick", EMPTY_ARGS)); - } else { - Callback.invokeAPICallbackUnsafe("tick", EMPTY_ARGS); - } - Updatable.getForServer().onTick(); - executor.blockUntilExecuted(); - } - - public static class Tick { - private static int tickCounter = 0; - - private static void resetTickCounter() { - tickCounter = 0; - } - - private final int time; - - private Tick(int time) { - this.time = time; - } - - private static Tick next() { - return new Tick(tickCounter++); - } - } -} diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/Updatable.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/Updatable.java index 604637b33..29d56a574 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/Updatable.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/Updatable.java @@ -1,16 +1,19 @@ package com.zhekasmirnov.innercore.api.runtime; -import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.apparatus.adapter.innercore.UserDialog; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; -import com.zhekasmirnov.innercore.api.runtime.saver.world.ScriptableSaverScope; import com.zhekasmirnov.innercore.api.runtime.saver.world.WorldDataScopeRegistry; import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.innercore.icstd.script.ScriptObjectComponent; +import org.innercore.icstd.updatable.UpdatableComponent; +import org.innercore.icstd.serialization.*; +import org.innercore.icstd.updatable.*; import org.mozilla.javascript.*; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; /** * Created by zheka on 10.08.2017. @@ -18,86 +21,127 @@ public class Updatable { private static final Object[] EMPTY_ARGS = new Object[]{}; + private final Object[] DT_SCRIPT_ARGS = new Object[1]; // 1 instance per scope - private final List updatableObjects = new ArrayList<>(); - private final List disabledDueToError = new ArrayList<>(); + private final List updatableObjects = new ArrayList<>(); + public final UpdatableSchedulerScope updatableScope; private Context currentContext = null; - private Updatable() {} + private Updatable(String scopeName) { + updatableScope = UpdatableScheduler.createNewScope(scopeName); + } public List getAllUpdatableObjects() { - return updatableObjects; + return updatableObjects.stream().map(u -> u.updatable).collect(Collectors.toList()); } - public void addUpdatable(ScriptableObject obj) { - Object _update = obj.get("update", obj); - if (_update instanceof Function) { - updatableObjects.add(obj); - } else { - throw new IllegalArgumentException("cannot add updatable object: .update must be a function, not " + (_update != null ? _update.getClass() : null) + " " + _update); + private void removeUpdatable(LegacyScriptUpdatable updatable) { + if (updatableObjects.get(updatable.index) != updatable) + throw new IllegalStateException(); + LegacyScriptUpdatable last = updatableObjects.get(updatableObjects.size() - 1); + if (last != updatable) { + updatableObjects.set(updatable.index, last); + last.index = updatable.index; } + updatableObjects.remove(updatableObjects.size() - 1); } - public void onTick() { - if (currentContext == null) { - currentContext = Compiler.assureContextForCurrentThread(); - } - // Keep amount to update a fixed number of objects, excluding new ones; as before, they will be executed on next game tick - int updatableAmount = updatableObjects.size(); - for (int offset = 0; currentContext != null && offset < updatableAmount; offset++) { - ScriptableObject updatable = updatableObjects.get(offset); + public class LegacyScriptUpdatable implements UpdatableComponent, ScriptObjectComponent + { + ScriptableObject updatable; + UpdatableHandle handle; + int index; + boolean isDisabledDueToError = false; + Object[] scriptDtArgs = new Object[] { 1 }; + + LegacyScriptUpdatable(ScriptableObject updatable, int index) { + this.updatable = updatable; + this.index = index; + Object _update = updatable.get("update", updatable); + if (!(_update instanceof Function)) { + throw new IllegalArgumentException("cannot add updatable object: .update must be a function, not " + (_update != null ? _update.getClass() : null) + " " + _update); + } + this.handle = updatableScope.create(this).setRecurringSchedule(1).schedule(1); + updatable.put("_handle", updatable, this.handle); + } + @Override + public void update(int dt) { + if (currentContext == null) { + currentContext = Compiler.assureContextForCurrentThread(); + } if (!ScriptableObjectHelper.getBooleanProperty(updatable, "noupdate", false)) { try { - ((Function) updatable.get("update")).call(currentContext, updatable.getParentScope(), updatable, EMPTY_ARGS); + DT_SCRIPT_ARGS[0] = dt; + ((Function) updatable.get("update")).call(currentContext, updatable.getParentScope(), updatable, DT_SCRIPT_ARGS); } catch (Throwable e) { - updatableObjects.remove(offset--); - updatableAmount--; - disabledDueToError.add(updatable); - + isDisabledDueToError = true; + handle.destroy(); Object _handle_error = updatable.get("_handle_error"); if (_handle_error instanceof Function) { Function handle_error = (Function) _handle_error; try { handle_error.call(currentContext, handle_error.getParentScope(), updatable, new Object[] { e }); - continue; + return; } catch (Throwable th) { - Logger.error("UPDATABLE ERROR", "Error occurred in error handler for " + updatableToString(currentContext, updatable) + " hash=" + updatable.hashCode(), th); + UserDialog.insignificantError("Error occurred in error handler for " + updatableToString(currentContext, updatable) + " hash=" + updatable.hashCode(), th, true); } } reportError(e, updatableToString(currentContext, updatable)); - continue; + return; } } if (ScriptableObjectHelper.getBooleanProperty(updatable, "remove", false)) { - updatableObjects.remove(offset--); - updatableAmount--; + handle.destroy(); + removeUpdatable(this); } } + + @Override + public Object getScriptObject() { + return updatable; + } + + } + + public void addUpdatable(ScriptableObject obj) { + updatableObjects.add(new LegacyScriptUpdatable(obj, updatableObjects.size())); } public void cleanUp() { updatableObjects.clear(); - disabledDueToError.clear(); currentContext = null; + updatableScope.clearAll(); + updatableScope.setCurrentTick(0); + } + + public void onTick(int tickNo) { + updatableScope.executeTickForScope(tickNo, -1); } // for backward compatibility + + @Deprecated + public void onTick() { + throw new RuntimeException("Deprecated"); + } + @Deprecated - public void onPostTick() {} + public void onPostTick() { + throw new RuntimeException("Deprecated"); + } @Deprecated public void onTickSingleThreaded() { - onTick(); - onPostTick(); + throw new RuntimeException("Deprecated"); } - private static final Updatable serverInstance = new Updatable(); - private static final Updatable clientInstance = new Updatable(); + private static final Updatable serverInstance = new Updatable("server"); + private static final Updatable clientInstance = new Updatable("local"); public static Updatable getForServer() { return serverInstance; @@ -108,7 +152,7 @@ public static Updatable getForClient() { } public static void reportError(Throwable err, String updatableStr) { - Logger.error("UPDATABLE ERROR", "Updatable " + updatableStr + " was disabled due to error, corresponding object will be disabled. To re-enable it re-enter the world.", err); + UserDialog.dialog("UPDATABLE ERROR", "Updatable " + updatableStr + " was disabled due to error, corresponding object will be disabled. To re-enable it re-enter the world.", err, true); } private static String updatableToString(Context ctx, Scriptable obj) { @@ -141,45 +185,41 @@ public static void setPreferences(int mode, int modeValue) { throw new IllegalArgumentException("invalid updatable engine mode: " + mode); } + public static void loadClass() {} + static { - WorldDataScopeRegistry.getInstance().addScope("_updatables", new ScriptableSaverScope() { + WorldDataScopeRegistry.getInstance().addScope("_updatables", new WorldDataScopeRegistry.SaverScope() { @Override - public ScriptableObject save() { - ArrayList updatableObjectsToSave = new ArrayList<>(); - for (ScriptableObject updatable : getForServer().updatableObjects) { - if (ObjectSaverRegistry.getSaverFor(updatable) != null) { - updatableObjectsToSave.add(updatable); - } - } - for (ScriptableObject updatable : getForServer().disabledDueToError) { - if (ObjectSaverRegistry.getSaverFor(updatable) != null && !ScriptableObjectHelper.getBooleanProperty(updatable, "remove", false)) { - updatableObjectsToSave.add(updatable); + public void write(IScopedWriter io, SerializationContext ctx) { + IArrayWriter array = io.asArray(); + for (LegacyScriptUpdatable updatable : getForServer().updatableObjects) { + if (ObjectSerializer.getSerializerTypeIdFromObject(updatable.updatable) != null) { + ObjectSerializer.serializeObject(array.beginScope(), updatable.updatable, ctx, true); } } - return new NativeArray(updatableObjectsToSave.toArray()); + io.endScope(); } @Override - public void read(Object scope) { + public void read(IScopedReader io, SerializationContext ctx) { + IArrayReader array = io.asArray(); int successfullyLoaded = 0; - if (scope instanceof NativeArray) { - Object[] updatableObjectsToRead = ((NativeArray) scope).toArray(); - for (Object possibleUpdatable : updatableObjectsToRead) { - if (possibleUpdatable instanceof ScriptableObject) { - ScriptableObject scriptableUpdatable = (ScriptableObject) possibleUpdatable; - if (scriptableUpdatable.get("update") instanceof Function) { - getForServer().addUpdatable(scriptableUpdatable); - successfullyLoaded++; - continue; - } + for (int i = 0; i < array.size(); i++) { + Object possibleUpdatable = ObjectSerializer.deserializeObject(array.beginScope(i), Object.class, ctx, true); + if (possibleUpdatable instanceof ScriptableObject) { + ScriptableObject scriptableUpdatable = (ScriptableObject) possibleUpdatable; + if (scriptableUpdatable.get("update") instanceof Function) { + getForServer().addUpdatable(scriptableUpdatable); + successfullyLoaded++; + continue; } - ICLog.i("UPDATABLE", "loaded updatable data is not a scriptable object or it does not have update function, loading failed. obj=" + possibleUpdatable); } - } else { - ICLog.i("UPDATABLE", "assertion failed: updatable scope is not an array, loading failed"); + if (possibleUpdatable != null) { + ctx.addError(null, possibleUpdatable, "loaded updatable data is not a scriptable object or it does not have update function, loading failed"); + } } - - ICLog.d("UPDATABLE", "successfully loaded updatables: " + successfullyLoaded); + ICLog.d("UPDATABLE", "successfully loaded " + successfullyLoaded + "/" + array.size() + " updatables"); + io.endScope(); } }); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/ArmorRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/ArmorRegistry.java index bbe6aaaaa..c46ffc29f 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/ArmorRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/ArmorRegistry.java @@ -1,9 +1,9 @@ package com.zhekasmirnov.innercore.api.runtime.other; import com.zhekasmirnov.innercore.api.NativeItem; -import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; import com.zhekasmirnov.innercore.api.commontypes.ScriptableParams; import com.zhekasmirnov.innercore.api.log.ICLog; +import org.innercore.icstd.inventory.ItemInstance; import org.mozilla.javascript.Context; import org.mozilla.javascript.ScriptableObject; diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/WorldGen.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/WorldGen.java index 2cd825f6f..3c0664e47 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/WorldGen.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/other/WorldGen.java @@ -1,14 +1,13 @@ package com.zhekasmirnov.innercore.api.runtime.other; -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; -import com.zhekasmirnov.apparatus.minecraft.version.MinecraftVersion; -import com.zhekasmirnov.apparatus.minecraft.version.MinecraftVersions; -import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.apparatus.adapter.innercore.UserDialog; import com.zhekasmirnov.innercore.api.NativeAPI; import com.zhekasmirnov.innercore.api.dimensions.CustomDimension; import com.zhekasmirnov.innercore.api.dimensions.CustomDimensionGenerator; import com.zhekasmirnov.innercore.api.log.ICLog; -import com.zhekasmirnov.innercore.api.runtime.Callback; +import org.innercore.icstd.event.EventController; +import org.innercore.icstd.event.Events; +import org.innercore.icstd.region.BlockSource; import java.util.ArrayList; import java.util.List; @@ -33,19 +32,17 @@ public class WorldGen { private static final int MODE_CUSTOM = 3; private static void invokeGenerateChunkCallback(String name, int chunkX, int chunkZ, int dimensionId) { - /* int */ long worldSeed = NativeAPI.getSeed(); + long worldSeed = NativeAPI.getSeed(); long seed = (long) worldSeed + (chunkX ^ 836430234L) * (chunkZ ^ 827774123L); Random random = new Random(seed); - List callbacks = Callback.getCallbackAsRunnableList(name, new Object[] { chunkX, chunkZ, random, - dimensionId, seed, worldSeed, worldSeed ^ (dimensionId * 131071) }); - for (Runnable callback : callbacks) { - try { - callback.run(); - } catch (Throwable err) { - Logger.error("error occurred in chunk generation " + chunkX + ", " + chunkZ - + " for dimension " + dimensionId, err); + + EventController ctrl = new EventController(); + ctrl.catchExceptions = true; + Events.invokeScriptEvent(ctrl, name, new Object[] {chunkX, chunkZ, random, dimensionId, seed, worldSeed, worldSeed ^ (dimensionId * 131071)}); + if (ctrl.errors != null) + for (Throwable err : ctrl.errors) { + UserDialog.insignificantError("error occurred in chunk generation " + chunkX + ", " + chunkZ + " for dimension " + dimensionId, err, true, true); } - } } private static void generateChunkByLevelAndMode(int chunkX, int chunkZ, int dimensionId, int level, int mode) { @@ -92,7 +89,7 @@ private static int dimensionIdToGenerationMode(int id) { } public static void generateChunk(int x, int z, int level) { - NativeBlockSource blockSource = NativeBlockSource.getCurrentWorldGenRegion(); + BlockSource blockSource = BlockSource.getCurrentWorldGenRegion(); int dimension = blockSource != null ? blockSource.getDimension() : -1; if (dimension == -1) { ICLog.i("ERROR", "generating chunk without block source"); diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/CriticalDataFile.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/CriticalDataFile.java new file mode 100644 index 000000000..841931db0 --- /dev/null +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/CriticalDataFile.java @@ -0,0 +1,88 @@ +package com.zhekasmirnov.innercore.api.runtime.saver; + +import com.zhekasmirnov.innercore.api.log.ICLog; + +import java.io.File; + +public class CriticalDataFile { + private final File mainFile; + private final File tmpFile; + private final File backupFile; + private enum State { IDLE, READING, READING_BACKUP, WRITING } + State state = State.IDLE; + + public CriticalDataFile(File mainFile) { + this.mainFile = mainFile; + this.tmpFile = new File(mainFile.getAbsolutePath() + ".tmp"); + this.backupFile = new File(mainFile.getAbsolutePath() + ".bak"); + } + + public synchronized File beginRead() { + if (state != State.IDLE) + throw new IllegalStateException("beginRead(): not in correct state " + state); + if (mainFile.exists()) { + state = State.READING; + return mainFile; + } + state = State.READING_BACKUP; + return backupFile; + } + + public synchronized File readBackupAfterMainFailed() { + if (state == State.READING_BACKUP) + return null; + if (state != State.READING) + throw new IllegalStateException("readBackupAfterMainFailed(): not in correct state " + state); + mainFile.delete(); + state = State.READING_BACKUP; + return backupFile; + } + + public synchronized void endRead() { + if (state != State.READING && state != State.READING_BACKUP) + throw new IllegalStateException("endRead(): not in correct state " + state); + state = State.IDLE; + } + + public synchronized File beginWrite() { + if (state != State.IDLE) + throw new IllegalStateException("beginWrite(): not in correct state " + state); + state = State.WRITING; + return tmpFile; + } + + public synchronized boolean endSuccessfulWrite(boolean writeToLog) { + if (state != State.WRITING) + throw new IllegalStateException("endSuccessfulWrite(): not in correct state " + state); + state = State.IDLE; + if (mainFile.isFile()) { // main file is valid + backupFile.delete(); + if (!mainFile.renameTo(backupFile)) { + if (writeToLog) ICLog.d("SAVER", "failed to rename " + mainFile.getAbsolutePath() + " to " + backupFile.getName()); + return false; + } + } else { + if (mainFile.exists() && !mainFile.delete()) + return false; + } + if (tmpFile.renameTo(mainFile)) { + if (writeToLog) ICLog.d("SAVER", "renamed saves file " + tmpFile.getAbsolutePath() + " to " + mainFile.getName()); + return true; + } else { + if (writeToLog) ICLog.d("SAVER", "failed to rename " + mainFile.getAbsolutePath() + " to " + backupFile.getName()); + return false; + } + } + + public synchronized void abortWrite() { + if (state != State.WRITING) + throw new IllegalStateException("abortWrite(): not in correct state " + state); + state = State.IDLE; + tmpFile.delete(); + } + + @Override + public String toString() { + return "CriticalDataFile{path=" + mainFile.getAbsolutePath() + "}"; + } +} diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/GlobalSaves.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/GlobalSaves.java deleted file mode 100644 index d755d62f6..000000000 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/GlobalSaves.java +++ /dev/null @@ -1,270 +0,0 @@ -package com.zhekasmirnov.innercore.api.runtime.saver; - -import com.zhekasmirnov.horizon.runtime.logger.Logger; - -/** - * Created by zheka on 20.08.2017. - */ - -import com.zhekasmirnov.innercore.api.NativeAPI; -import com.zhekasmirnov.innercore.api.log.ICLog; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import com.zhekasmirnov.innercore.api.runtime.Callback; -import com.zhekasmirnov.innercore.api.runtime.LevelInfo; -import com.zhekasmirnov.innercore.api.runtime.MainThreadQueue; -import com.zhekasmirnov.innercore.utils.FileTools; -import org.json.JSONException; -import org.mozilla.javascript.ScriptableObject; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; - -public class GlobalSaves { - public static final String SAVES_FILE_NAME = "moddata.json"; - public static final String SAVES_RESERVE_FILE_NAME = "moddata_backup.json"; - - private static ScriptableObject globalScope = null; - - private static boolean isBeautified = true; - - private static void throwError(Throwable e) { - RuntimeException r = new RuntimeException("error occurred in global saves"); - r.initCause(e); - throw r; - } - - private static void readScope(String fileName) { - String dir = LevelInfo.getAbsoluteDir(); - if (dir == null) { - return; - } - - globalScope = null; - - String jsonStr = null; - try { - jsonStr = FileTools.readFileText(dir + fileName); - } catch (IOException e) { - throwError(e); - } - - try { - globalScope = (ScriptableObject) JsonHelper.parseJsonString(jsonStr); - } catch (JSONException | ClassCastException e) { - throwError(e); - } - } - - private static void writeScope() { - String dir = LevelInfo.getAbsoluteDir(); - if (dir == null || globalScope == null) { - return; - } - - try { - String text = JsonHelper.scriptableToJsonString(globalScope, isBeautified); - FileTools.writeFileText(dir + SAVES_FILE_NAME, text); - FileTools.writeFileText(dir + SAVES_RESERVE_FILE_NAME, text); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private static class LoggedSavesError { - final String message; - final Throwable error; - - LoggedSavesError(String message, Throwable error) { - this.message = message; - this.error = error; - } - } - - private static ArrayList savesErrors = new ArrayList<>(); - - public static void logSavesError(String message, Throwable err) { - savesErrors.add(new LoggedSavesError(message, err)); - } - - public static void clearSavesErrorLog() { - savesErrors.clear(); - } - - public static ArrayList getSavesErrorLog() { - return savesErrors; - } - - public static void showSavesErrorsDialogIfRequired(boolean isReading) { - if (!savesErrors.isEmpty()) { - StringBuilder log = new StringBuilder(); - for (LoggedSavesError err : savesErrors) { - log.append(err.message).append("\n").append(Logger.getStackTrace(err.error)).append("\n\n"); - } - Logger.error( - "Errors occurred while " + (isReading ? "reading" : "saving") + " data:\n\n" + log, - "SOME " + (isReading ? "READING" : "SAVING") + " ERRORS OCCURRED"); - } - } - - private static HashMap globalScopeMap = new HashMap<>(); - - public static void registerScope(String name, GlobalSavesScope scope) { - while (globalScopeMap.containsKey(name)) { - name += name.hashCode() & 0xFF; - } - - scope.setName(name); - globalScopeMap.put(name, scope); - } - - public synchronized static void readSaves() { - ICLog.d("SAVES", "reading saves..."); - updateAutoSaveTime(); - - try { - clearSavesErrorLog(); - readScope(SAVES_FILE_NAME); - } catch (RuntimeException e) { - try { - clearSavesErrorLog(); - readScope(SAVES_RESERVE_FILE_NAME); - } catch (RuntimeException e2) { - ICLog.e("SAVES", "failed to read saves", e2); - globalScope = ScriptableObjectHelper.createEmpty(); - } - } - - for (String name : globalScopeMap.keySet()) { - Object saverScope; - if (globalScope.has(name, globalScope)) { - saverScope = globalScope.get(name); - } else { - saverScope = ScriptableObjectHelper.createEmpty(); - } - - try { - globalScopeMap.get(name).read(saverScope); - } catch (Exception err) { - logSavesError("error in reading scope " + name, err); - } - } - showSavesErrorsDialogIfRequired(true); - - Callback.invokeAPICallback("ReadSaves", globalScope); - } - - public synchronized static void writeSaves() { - ICLog.d("SAVES", "writing saves..."); - updateAutoSaveTime(); - - globalScope = ScriptableObjectHelper.createEmpty(); - - clearSavesErrorLog(); - for (String name : globalScopeMap.keySet()) { - try { - globalScope.put(name, globalScope, globalScopeMap.get(name).save()); - } catch (Exception err) { - logSavesError("error in writing saves scope " + name, err); - } - } - - Callback.invokeAPICallback("WriteSaves", globalScope); - - try { - writeScope(); - } catch (RuntimeException e) { - ICLog.e("SAVES", "failed to write saves", e); - } - showSavesErrorsDialogIfRequired(false); - } - - private static Thread currentThread = null; - private static int currentThreadQueueSize = 0; - - private static boolean isReadComplete = false; - - public static boolean isReadComplete() { - return isReadComplete; - } - - public static void setIsReadComplete(boolean isReadComplete) { - GlobalSaves.isReadComplete = isReadComplete; - } - - public static void writeSavesInThread(final boolean saveMCPEWorld) { - currentThreadQueueSize++; - - if (currentThread == null) { - currentThread = new Thread(new Runnable() { - @Override - public void run() { - long start, end; - while (currentThreadQueueSize > 0) { - ICLog.d("SAVES", "started saving world data"); - ICLog.flush(); - - MainThreadQueue.serverThread.enqueue(new Runnable() { - public void run() { - if (saveMCPEWorld) { - ICLog.d("SAVES", "saving minecraft world..."); - ICLog.flush(); - long start = System.currentTimeMillis(); - NativeAPI.forceLevelSave(); - long end = System.currentTimeMillis(); - ICLog.d("SAVES", "saving minecraft world in thread took " + (end - start) + " ms"); - } - } - }); - - start = System.currentTimeMillis(); - writeSaves(); - end = System.currentTimeMillis(); - ICLog.d("SAVES", "saving mod data in thread took " + (end - start) + " ms"); - - ICLog.flush(); - currentThreadQueueSize--; - } - currentThread = null; - } - }); - currentThread.start(); - } - } - - public static void sleepUntilThreadEnd() { - long start = System.currentTimeMillis(); - while (currentThread != null) { - try { - Thread.sleep(50); - } catch (InterruptedException e) { - } - } - long end = System.currentTimeMillis(); - ICLog.d("SAVES", "delaying main thread while saving data took " + (end - start) + " ms"); - } - - private static boolean autoSaveEnabled = false; - private static int autoSavePeriod = 30000; - private static long lastAutoSave = 0; - - public static void setAutoSaveParams(boolean enabled, int period) { - autoSaveEnabled = enabled; - autoSavePeriod = Math.max(5000, period); - ICLog.d("SAVES", "auto-save params set enabled=" + enabled + " period=" + period); - } - - private static void updateAutoSaveTime() { - lastAutoSave = System.currentTimeMillis(); - } - - public static void startAutoSaveIfNeeded() { - if (autoSaveEnabled) { - long time = System.currentTimeMillis(); - if (time - lastAutoSave > autoSavePeriod) { - updateAutoSaveTime(); - writeSavesInThread(true); - } - } - } -} diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/GlobalSavesScope.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/GlobalSavesScope.java index d2944ebcd..383691f2e 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/GlobalSavesScope.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/GlobalSavesScope.java @@ -16,6 +16,5 @@ public void setName(String name) { } public abstract Object save(); - public abstract void read(Object scope); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/JsonHelper.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/JsonHelper.java deleted file mode 100644 index 36d8b8b8e..000000000 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/JsonHelper.java +++ /dev/null @@ -1,236 +0,0 @@ -package com.zhekasmirnov.innercore.api.runtime.saver; - -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.mozilla.javascript.NativeArray; -import org.mozilla.javascript.Scriptable; -import org.mozilla.javascript.ScriptableObject; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * Created by zheka on 19.08.2017. - */ - -public class JsonHelper { - - private static class JsonToString { - private StringBuilder result; - - private boolean beautify = false; - private int depth = 0; - - private ArrayList isArrayStack = new ArrayList<>(); - - public JsonToString(boolean beautify) { - this.result = new StringBuilder(); - this.beautify = beautify; - this.depth = 0; - } - - private boolean isArray() { - if (isArrayStack.size() > 0) { - return isArrayStack.get(0); - } - return false; - } - - private void push(boolean isArray) { - isArrayStack.add(0, isArray); - depth++; - } - - private void pop() { - if (isArrayStack.size() == 0 || depth < 1) { - throw new IllegalArgumentException("excess object or array end"); - } - depth--; - isArrayStack.remove(0); - } - - private String getIntend() { - String intend = ""; - if (!beautify) { - return intend; - } - for (int i = 0; i < depth; i++) { - intend += " "; - } - return intend; - } - - private void putCommaIfNeeded() { - int index = this.result.length() - 1; - while (index != -1) { - char c = this.result.charAt(index); - if (c != ' ' && c != '\n') { - break; - } - index--; - } - if (index == -1) { - return; - } - - char last = this.result.charAt(index); - if (last != ',' && last != '{' && last != '[') { - this.result.append(","); - } - } - - private void begin(boolean isArray) { - this.result.append(isArray ? "[" : "{").append(beautify ? "\n" : ""); - push(isArray); - } - - private void end() { - boolean isArr = isArray(); - pop(); - this.result.append(beautify ? "\n" + getIntend() : "").append(isArr ? "]" : "}"); - } - - public void key(Object key) { - putCommaIfNeeded(); - - if (beautify && this.result.charAt(this.result.length() - 1) != '\n') { - this.result.append("\n"); - } - - if (beautify) { - this.result.append(getIntend()); - } - - if (!isArray()) { - this.result.append("\"").append(key).append("\":"); - } - } - - private static DecimalFormat format = new DecimalFormat("#"); - - public void value(Object val) { - if (val == null) { - throw new IllegalArgumentException("value cannot be null"); - } - - String strVal; - - if (val instanceof CharSequence) { - String str = val.toString(); - str = str.replace("\n", "\\n"); - str = str.replace("\"", "\\\""); - strVal = "\"" + str + "\""; - } else if (val instanceof Number && ((Number) val).doubleValue() == ((Number) val).longValue()) { - strVal = format.format(val); - } else { - strVal = val.toString(); - } - - this.result.append(strVal); - } - - public String getResult() { - return result.toString(); - } - } - - private static HashMap scriptableByHashCode = new HashMap<>(); - - public static synchronized String scriptableToJsonString(ScriptableObject scriptableObject, boolean beautify) { - if (scriptableObject == null) { - return "{}"; - } - scriptableByHashCode.clear(); - JsonToString jsonToString = new JsonToString(beautify); - stringify(jsonToString, scriptableObject); - return jsonToString.getResult(); - } - - private static void stringify(JsonToString jsonToString, ScriptableObject scriptableObject) { - scriptableByHashCode.put(scriptableObject.hashCode(), scriptableObject); - - boolean isArray = scriptableObject instanceof NativeArray; - - jsonToString.begin(isArray); - - Object[] keys = scriptableObject.getAllIds(); - for (Object key : keys) { - if (isArray && key instanceof CharSequence) { - continue; - } - - Object val = scriptableObject.get(key); - if (val != null) { - val = ObjectSaverRegistry.unwrapIfNeeded(val); - if (val instanceof ScriptableObject) { - if (scriptableByHashCode.containsKey(val.hashCode())) { - continue; - } - val = ObjectSaverRegistry.saveObjectAndCheckSaveIgnoring(val); - if (val != null) { - jsonToString.key(key); - stringify(jsonToString, (ScriptableObject) val); - } - } else if (val instanceof CharSequence || val instanceof Number || val instanceof Boolean) { - jsonToString.key(key); - jsonToString.value(val); - } else { - ScriptableObject saveResult = ObjectSaverRegistry.saveObject(val); - if (saveResult != null) { - jsonToString.key(key); - stringify(jsonToString, saveResult); - } - } - } - } - - jsonToString.end(); - } - - public static Scriptable parseJsonString(String string) throws JSONException { - JSONObject json = new JSONObject(string); - return jsonToScriptable(json); - } - - public static Scriptable jsonToScriptable(Object object) { - ScriptableObject scriptable; - if (object instanceof JSONArray) { - JSONArray arr = (JSONArray) object; - List list = new ArrayList<>(); - - for (int i = 0; i < arr.length(); i++) { - Object val = arr.opt(i); - if (val instanceof JSONObject || val instanceof JSONArray) { - val = jsonToScriptable(val); - } - list.add(val); - } - - scriptable = ScriptableObjectHelper.createArray(list); - } else if (object instanceof JSONObject) { - scriptable = ScriptableObjectHelper.createEmpty(); - JSONObject json = (JSONObject) object; - JSONArray arr = json.names(); - - if (arr != null) { - for (int i = 0; i < arr.length(); i++) { - String key = arr.optString(i); - Object val = json.opt(key); - if (val instanceof JSONObject || val instanceof JSONArray) { - val = jsonToScriptable(val); - } - scriptable.put(key, scriptable, val); - } - } - } else { - throw new IllegalArgumentException( - "FAILED ASSERTION: JsonHelper.jsonToScriptable can get only JSONObject or JSONArray"); - } - - return ObjectSaverRegistry.readObject(scriptable); - } -} diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaver.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaver.java index c3a0d54dd..8be5813b2 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaver.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaver.java @@ -1,12 +1,12 @@ package com.zhekasmirnov.innercore.api.runtime.saver; -import org.mozilla.javascript.ScriptableObject; +import org.innercore.icstd.serialization.ScriptableObjectSerializer; /** * Created by zheka on 20.08.2017. */ -public abstract class ObjectSaver { +public abstract class ObjectSaver extends ScriptableObjectSerializer { private int saverId = 0; public int getSaverId() { @@ -16,8 +16,4 @@ public int getSaverId() { public void setSaverId(int saverId) { this.saverId = saverId; } - - public abstract Object read(ScriptableObject input); - - public abstract ScriptableObject save(Object input); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaverRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaverRegistry.java index f9f9c61b8..0831b867d 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaverRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/ObjectSaverRegistry.java @@ -1,62 +1,57 @@ package com.zhekasmirnov.innercore.api.runtime.saver; import com.zhekasmirnov.horizon.runtime.logger.Logger; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.api.mod.ui.container.Container; -import com.zhekasmirnov.innercore.api.runtime.saver.world.WorldDataSaver; +import org.innercore.icstd.serialization.IObjectSerializer; +import org.innercore.icstd.serialization.ObjectSerializer; +import org.json.JSONException; import org.json.JSONObject; import org.mozilla.javascript.*; -import java.util.HashMap; -import java.util.Map; - /** * Created by zheka on 19.08.2017. */ public class ObjectSaverRegistry { + @Deprecated public static final String PROPERTY_IGNORE_SAVE = "_json_ignore"; public static final String PROPERTY_SAVER_ID = "_json_saver_id"; - public static final String PROPERTY_SAVER_ENABLE = "_json_saver_enable"; - - private static final ScriptableObject scope; - - private static HashMap saverMap = new HashMap<>(); - //private static HashMap saverByObjectHash = new HashMap<>(); - private static HashMap saverNameById = new HashMap<>(); + @Deprecated + public static final String PROPERTY_SAVER_ENABLE = PROPERTY_SAVER_ID;//"_json_saver_enable"; static { - scope = Context.enter().initStandardObjects(); - Container.initSaverId(); - NativeItemInstanceExtra.initSaverId(); + ItemInstanceExtra.initSaverId(); } + @Deprecated public interface IHashSaver { - int getId(); - void setId(int id); + @Deprecated + int getObjectSaverId(); + @Deprecated + void setObjectSaverId(int id); } - public static int registerSaver(String name, ObjectSaver saver) { - int saverId = name.hashCode(); - while (saverMap.containsKey(saverId)) { - saverId++; - } - - saverMap.put(saverId, saver); - saverNameById.put(saverId, name); - saver.setSaverId(saverId); + public static String registerSaverStr(String name, ObjectSaver saver) { + ObjectSerializer.registerType(name, Object.class, saver, false); + saver.setSaverId(ObjectSerializer.getLegacyHashIdFromTypeId(name, -1)); + return name; + } - return saverId; + @Deprecated + public static int registerSaver(String name, ObjectSaver saver) { + registerSaverStr(name, saver); + return saver.getSaverId(); } + @Deprecated public static String getSaverName(int saverId) { - return saverNameById.get(saverId); + return ObjectSerializer.getTypeIdFromLegacyHashId(saverId, null); } - - + @Deprecated static Object unwrapIfNeeded(Object object) { if (object instanceof Wrapper) { return ((Wrapper) object).unwrap(); @@ -64,122 +59,47 @@ static Object unwrapIfNeeded(Object object) { return object; } + @Deprecated public static ObjectSaver getSaverFor(Object object) { - ObjectSaver saver = null; - if(object instanceof ScriptableObject scriptable){ - int id = ScriptableObjectHelper.getIntProperty(scriptable, PROPERTY_SAVER_ENABLE, -1); - if(id != -1) - saver = saverMap.get(id); - }else if(object instanceof IHashSaver hashSaver){ - saver = saverMap.get(hashSaver.getId()); - }else if(object instanceof JSONObject json && json.has(PROPERTY_SAVER_ENABLE)){ - saver = saverMap.get(json.getInt(PROPERTY_SAVER_ENABLE)); - } - if (saver != null) { - return saver; - } - if (object instanceof ScriptableObject) { - Object val = ((ScriptableObject) object).get(PROPERTY_SAVER_ID); - if (val instanceof Number) { - int id = ((Number) val).intValue(); - return saverMap.get(id); + String typeId = ObjectSerializer.getSerializerTypeIdFromObject(object); + if (typeId != null) { + IObjectSerializer serializer = ObjectSerializer.getSerializerByTypeId(typeId); + if (serializer instanceof ObjectSaver) { + return (ObjectSaver) serializer; } } return null; } - public static ScriptableObject saveObject(Object object) { - object = unwrapIfNeeded(object); - - ObjectSaver saver = getSaverFor(object); - if (saver != null) { - ScriptableObject result = null; - try { - result = saver.save(object); - } catch(Throwable err) { - WorldDataSaver.logErrorStatic("error in saving object of saver type " + getSaverName(saver.getSaverId()), err); - return null; - } - if (result != null) { - result.put(PROPERTY_SAVER_ID, result, saver.getSaverId()); - } - return result; + @Deprecated + public static void registerObject(Object object, String saverId) { + if (!ObjectSerializer.isTypeRegistered(saverId)) { + throw new IllegalArgumentException("no saver found for id " + saverId + " use only registerObjectSaver return values"); } - return object instanceof ScriptableObject ? (ScriptableObject) object : null; - } - - // unwrap object if required, run saver, if exists, or just return object itself - public static Object saveOrSkipObject(Object object) { object = unwrapIfNeeded(object); - if (object == null || object == Undefined.instance) { - return null; - } - - ObjectSaver saver = getSaverFor(object); - if (saver != null) { - ScriptableObject result = null; - try { - result = saver.save(object); - } catch(Throwable err) { - WorldDataSaver.logErrorStatic("error in saving object of saver type " + getSaverName(saver.getSaverId()), err); - return null; - } - if (result != null) { - result.put(PROPERTY_SAVER_ID, result, saver.getSaverId()); - } - return result; - } - return object; - } - - public static ScriptableObject saveObjectAndCheckSaveIgnoring(Object object) { - if (object instanceof ScriptableObject) { - Object val = ((ScriptableObject) object).get(PROPERTY_IGNORE_SAVE); - if (val instanceof Boolean && (Boolean) val) { - return null; - } - } - return saveObject(object); - } - - public static Scriptable readObject(ScriptableObject object) { - ObjectSaver saver = getSaverFor(object); - if (saver != null) { - Object result = null; + if (object instanceof ScriptableObject){ + final ScriptableObject scriptable = (ScriptableObject) object; + scriptable.put(PROPERTY_SAVER_ID, scriptable, saverId); + }else if(object instanceof JSONObject){ try { - result = saver.read(object); - } catch(Throwable err) { - WorldDataSaver.logErrorStatic("error in reading object of saver type " + getSaverName(saver.getSaverId()), err); - return null; + ((JSONObject) object).put(PROPERTY_SAVER_ID, saverId); + } catch (JSONException e) { + ICLog.e("SAVER", "error get PROPERTY_SAVER_ENABLE", e); } - if (!(result instanceof Scriptable)) { - result = Context.javaToJS(result, scope); - if (!(result instanceof Scriptable)) { - return null; - } + }else if(object instanceof IHashSaver){ + int legacySaverId = ObjectSerializer.getLegacyHashIdFromTypeId(saverId, -1); + if (legacySaverId == -1) { + legacySaverId = ObjectSerializer.getLegacyHashIdFromTypeId(ObjectSerializer.SKIP_SERIALIZATION_TYPE_ID, -1); } - return (Scriptable) result; - } - return object; - } - - public static void registerObject(Object object, int saverId) { - if (!saverMap.containsKey(saverId)) { - throw new IllegalArgumentException("no saver found for id " + saverId + " use only registerObjectSaver return values"); - } - object = unwrapIfNeeded(object); - if (object instanceof ScriptableObject scriptable){ - scriptable.put(PROPERTY_SAVER_ENABLE, scriptable, saverId); - }else if(object instanceof JSONObject json){ - json.put(PROPERTY_SAVER_ENABLE, saverId); - }else if(object instanceof IHashSaver hashSaver){ - hashSaver.setId(saverId); + ((IHashSaver) object).setObjectSaverId(legacySaverId); }else { - Logger.warning("Error registerObject "+object.getClass().getName()+", serverId: "+saverId); + Logger.warning("Error registerObject "+ object.getClass().getName() + ", serverId: "+saverId); } } + @Deprecated public static void setObjectIgnored(ScriptableObject object, boolean ignore) { - object.put(PROPERTY_IGNORE_SAVE, object, ignore); + // NOTE: This is not working for a long time already + // object.put(PROPERTY_IGNORE_SAVE, object, ignore); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/serializer/ScriptableSerializer.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/serializer/ScriptableSerializer.java index 38192305a..95dcb4add 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/serializer/ScriptableSerializer.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/serializer/ScriptableSerializer.java @@ -1,121 +1,41 @@ package com.zhekasmirnov.innercore.api.runtime.saver.serializer; -import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; -import org.json.JSONArray; +import org.innercore.internal.serialization.JsonIO; +import org.innercore.icstd.serialization.ScriptableObjectSerializer; +import org.innercore.icstd.serialization.SerializationContext; import org.json.JSONException; import org.json.JSONObject; -import org.mozilla.javascript.*; - -import java.util.*; public class ScriptableSerializer { - public interface SerializationErrorHandler { - void handle(Exception err); + public static String scriptableToJsonString(Object object, SerializationContext ctx) { + JsonIO.ScopedWriter io = new JsonIO.ScopedWriter(); + ScriptableObjectSerializer.writeScriptable(io, object, ctx); + io.forceEndScope(ctx); + return jsonToString(io.getResult()); } - private static Object avoidInvalidJsonValues(Object object) { - if (object instanceof Undefined) { + public static Object scriptableFromJsonString(String jsonString, SerializationContext ctx, boolean canFail) { + Object json; + try { + json = stringToJson(jsonString); + } catch (JSONException e) { + if (!canFail) throw new RuntimeException(e); + ctx.addError(e, jsonString, "failed to deserialize json"); return null; } - if (object instanceof Number) { - Number number = (Number) object; - double d = number.doubleValue(); - float f = number.floatValue(); - if (Double.isNaN(d) || Float.isNaN(f) || Double.isInfinite(d) || Float.isInfinite(f)) { - return 0.0f; - } - } - return object; + JsonIO.ScopedReader io = new JsonIO.ScopedReader(json); + Object result = ScriptableObjectSerializer.readScriptable(io, ctx); + io.forceEndScope(ctx); + return result; } - private static Object scriptableToJson0(Object input, SerializationErrorHandler handler, Set iteratedObjects) { - // this will unwrap object if required, run saver, if exists, or just return object itself - input = ObjectSaverRegistry.saveOrSkipObject(input); - - // if instance of scriptable, serialize to json, otherwise return as it is - if (input instanceof ScriptableObject) { - ScriptableObject scriptableObject = (ScriptableObject) input; - - // check for cyclic reference - if (iteratedObjects.contains(scriptableObject)) { - return null; - } - iteratedObjects.add(scriptableObject); - // if it an array - if (scriptableObject instanceof NativeArray) { - JSONArray json = new JSONArray(); - for (Object element : ((NativeArray) scriptableObject).toArray()) { - // iterate array and serialize every element - json.put(scriptableToJson0(element, handler, iteratedObjects)); - } - - // remove from iterated and return - iteratedObjects.remove(scriptableObject); - return json; - } else { - // if it is object - JSONObject json = new JSONObject(); - for (Object key : scriptableObject.getIds()) { - Object value = scriptableObject.get(key); - try { - // iterate and serialize every value - json.put(key + "", scriptableToJson0(value, handler, iteratedObjects)); - } catch (JSONException e) { - if (handler != null) { - handler.handle(e); - } - } - } - - // remove from iterated and return - iteratedObjects.remove(scriptableObject); - return json; - } - } else { - return avoidInvalidJsonValues(input); - } - } - - // receives scriptable or plain object, that should be serialized using scriptable based serialization and returns resulting json, base type value or null - // this method will replace cycle references with null - public static Object scriptableToJson(Object object, SerializationErrorHandler handler) { - return scriptableToJson0(object, handler, new HashSet<>()); - } - - // receives base type value, json object or array and deserializes it into scriptable or any other object type, by using scriptable based serialization - public static Object scriptableFromJson(Object object) { - if (object instanceof JSONObject) { - JSONObject json = (JSONObject) object; - ScriptableObject scriptable = ScriptableObjectHelper.createEmpty(); - for (Iterator it = json.keys(); it.hasNext(); ) { - String key = it.next(); - Object value = scriptableFromJson(json.opt(key)); - scriptable.put(key, scriptable, value); - try{ - scriptable.put(Integer.parseInt(key), scriptable, value); - }catch (Exception ignore){} - } - return ObjectSaverRegistry.readObject(scriptable); - } else if (object instanceof JSONArray) { - JSONArray json = (JSONArray) object; - List array = new ArrayList<>(); - for (int i = 0; i < json.length(); i++) { - array.add(scriptableFromJson(json.opt(i))); - } - return ScriptableObjectHelper.createArray(array); - } else { - // assume this is a basic type object - return object; - } - } - public static String jsonToString(Object json) { + private static String jsonToString(Object json) { return "" + json; } - public static Object stringToJson(String str) throws JSONException { + private static Object stringToJson(String str) throws JSONException { if (str == null) { return null; } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/BinarySaverScope.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/BinarySaverScope.java index af8069a89..761f46a61 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/BinarySaverScope.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/BinarySaverScope.java @@ -1,5 +1,8 @@ package com.zhekasmirnov.innercore.api.runtime.saver.world; +import org.innercore.icstd.serialization.IScopedReader; +import org.innercore.icstd.serialization.IScopedWriter; +import org.innercore.icstd.serialization.SerializationContext; import org.json.JSONObject; import java.io.*; @@ -7,27 +10,31 @@ public abstract class BinarySaverScope implements WorldDataScopeRegistry.SaverScope { @Override - public void readJson(Object json) throws Exception { - String data = ((JSONObject) json).optString("data"); - if (data == null) { - throw new IOException("missing binary data for BinarySaverScope"); - } - try (ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data))) { - read(inputStream); + public void read(IScopedReader io, SerializationContext ctx) { + if (io.isObject()) { + String data = io.asObject().getString("data", null); + if (data != null) { + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data))) { + read(inputStream); + } catch (IOException e) { + ctx.addError(e, null, "failed to read binary from base64 data"); + } + } } + io.endScope(); } @Override - public Object saveAsJson() throws Exception { - JSONObject json = new JSONObject(); + public void write(IScopedWriter io, SerializationContext ctx) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { save(outputStream); - json.put("data", Base64.getEncoder().encodeToString(outputStream.toByteArray())); + io.asObject().putString("data", Base64.getEncoder().encodeToString(outputStream.toByteArray())); + } catch (IOException e) { + ctx.addError(e, null, "failed to save binary to base64 data"); } - return json; + io.endScope(); } public abstract void read(InputStream inputStream); - public abstract void save(OutputStream outputStream); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/ScriptableSaverScope.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/ScriptableSaverScope.java index bc39f5298..9ca0ac95f 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/ScriptableSaverScope.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/ScriptableSaverScope.java @@ -1,36 +1,29 @@ package com.zhekasmirnov.innercore.api.runtime.saver.world; -import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; -import com.zhekasmirnov.innercore.api.runtime.saver.serializer.ScriptableSerializer; +import org.innercore.icstd.serialization.*; import org.mozilla.javascript.Scriptable; -import java.io.IOException; public abstract class ScriptableSaverScope implements WorldDataScopeRegistry.SaverScope { @Override - public void readJson(Object json) throws Exception { - Object scriptable = ScriptableSerializer.scriptableFromJson(json); - if (scriptable instanceof Scriptable) { - read((Scriptable) scriptable); - }else if(scriptable == null){ - Logger.warning("scriptable saver scope readJson() de-serialized into non scriptable, use default value"); - read(getDefaultSaves()); - }else{ - throw new IOException("scriptable saver scope readJson() de-serialized into non scriptable "+scriptable.getClass()); + public void read(IScopedReader io, SerializationContext ctx) { + Object deserialized = ScriptableObjectSerializer.readScriptable(io, ctx); + if (!(deserialized instanceof Scriptable)) { + deserialized = getDefaultSaves(); } + read(deserialized); } @Override - public Object saveAsJson() throws Exception { - // an ugly solution, but a pretty functional one - return ScriptableSerializer.scriptableToJson(save(), err -> { - WorldDataSaver.logErrorStatic("error in serializer", err); - }); + public void write(IScopedWriter io, SerializationContext ctx) { + Object serialized = save(); + ScriptableObjectSerializer.writeScriptable(io, serialized, ctx); } public Scriptable getDefaultSaves(){ return ScriptableObjectHelper.createEmpty(); } + public abstract void read(Object object); public abstract Object save(); } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaver.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaver.java index ff0f2919c..319b16c28 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaver.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaver.java @@ -1,8 +1,14 @@ package com.zhekasmirnov.innercore.api.runtime.saver.world; +import com.zhekasmirnov.apparatus.adapter.innercore.UserDialog; +import com.zhekasmirnov.apparatus.util.Java8BackComp; import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.horizon.util.FileUtils; import com.zhekasmirnov.innercore.api.log.DialogHelper; +import com.zhekasmirnov.innercore.api.log.ICLog; +import com.zhekasmirnov.innercore.api.runtime.saver.CriticalDataFile; +import org.innercore.icstd.serialization.ObjectSerializerDebug; +import org.innercore.icstd.serialization.SerializationContext; import org.json.JSONException; import org.json.JSONObject; @@ -20,27 +26,19 @@ public enum SaverState { SAVE } - private static class LoggedSavesError { - final String message; - final Throwable error; - - LoggedSavesError(String message, SaverState state, Throwable error) { - this.message = message; - this.error = error; - } - } - - private static final String[] worldSavesFileNames = new String[] { "moddata.json", "moddata_backup.json" }; + private static final String MAIN_FILE_NAME = "moddata.json"; + private static final String LEGACY_MAIN_SAVES_BACKUP = "moddata_backup.json"; private final File worldDirectory; - private final List errorLog = new ArrayList<>(); + private final CriticalDataFile savesFile; private final Map missingScopeData = new HashMap<>(); private SaverState state = SaverState.IDLE; - + private boolean savingErrorsReportedAsDialog = false; public WorldDataSaver(File worldDirectory) { this.worldDirectory = worldDirectory; + savesFile = new CriticalDataFile(new File(worldDirectory, MAIN_FILE_NAME)); } public File getWorldDirectory() { @@ -55,47 +53,53 @@ private JSONObject readJsonWithLockCheck(File directory, String name, boolean lo File file = new File(directory, name); if (file.exists()) { if (!lockCheck || !FileUtils.getFileFlag(directory, name + "-opened")) { - try { - return FileUtils.readJSON(file); - } catch (IOException | JSONException exception) { - Logger.debug("FAILED TO READ SAVES", "Failed to read this world saves", exception); - } + return readJsonImpl(new File(directory, name)); } } return null; } + private JSONObject readJsonImpl(File file) { + if (file == null || !file.exists()) { + return null; + } + try { + ICLog.i("SAVER", "reading saves from " + file.getAbsolutePath()); + return FileUtils.readJSON(file); + } catch (IOException | JSONException exception) { + ICLog.e("SAVER", "Error reading saves from " + file.getAbsolutePath(), exception); + UserDialog.dialog("FAILED TO READ SAVES", "Failed to read this world main saves file " + file.getName(), exception, false); + return null; + } + } + // read all data on current thread public synchronized void readAllData(boolean showLogOnError) { if (worldDirectory == null) { return; } + savingErrorsReportedAsDialog = false; - JSONObject json = null; - for (String name : worldSavesFileNames) { - json = readJsonWithLockCheck(worldDirectory, name, true); - if (json != null) { - break; - } + JSONObject json = readJsonImpl(savesFile.beginRead()); + if (json == null) { + json = readJsonImpl(savesFile.readBackupAfterMainFailed()); } + savesFile.endRead(); if (json == null) { - json = readJsonWithLockCheck(worldDirectory, "moddata.json", false); - if (json == null) { - return; - } + json = readJsonWithLockCheck(worldDirectory, LEGACY_MAIN_SAVES_BACKUP, false); + } + if (json == null) { + return; } state = SaverState.READ; missingScopeData.clear(); - WorldDataScopeRegistry.getInstance().readAllScopes( - json, - (scope, error) -> logError("While reading scope " + scope, SaverState.READ, error), - missingScopeData::put - ); + SerializationContext ctx = new SerializationContext(); + WorldDataScopeRegistry.getInstance().readAllScopes(json, ctx, missingScopeData); state = SaverState.IDLE; if (showLogOnError) { - showAndClearErrorLog(SaverState.SAVE); + showErrorLog(SaverState.READ, ctx); } } @@ -113,61 +117,94 @@ public synchronized void saveAllData(boolean showLogOnError) { state = SaverState.SAVE; JSONObject json = new JSONObject(missingScopeData); - WorldDataScopeRegistry.getInstance().saveAllScopes( - json, - (scope, error) -> logError("While saving scope " + scope, SaverState.SAVE, error) - ); + SerializationContext ctx = new SerializationContext(); + WorldDataScopeRegistry.getInstance().saveAllScopes(json, ctx); state = SaverState.IDLE; - for (String name : worldSavesFileNames) { - try { - FileUtils.setFileFlag(worldDirectory, name + "-opened", true); - FileUtils.writeJSON(new File(worldDirectory, name), json); - FileUtils.setFileFlag(worldDirectory, name + "-opened", false); - } catch (Exception exception) { - Logger.debug("FAILED TO WRITE SAVES", "Failed to write this world saves", exception); - } + File fileToWrite = savesFile.beginWrite(); + try { + ICLog.i("SAVER", "writing saves to " + fileToWrite.getAbsolutePath()); + FileUtils.writeJSON(fileToWrite, json); + if (!savesFile.endSuccessfulWrite(true)) + throw new RuntimeException("Failed finalize saving to " + savesFile); + } catch (Throwable exception) { + ICLog.e("SAVER", "failed to write saves to " + savesFile, exception); + UserDialog.dialog("FAILED TO WRITE SAVES", "Failed to write this world saves", exception, false); + savesFile.abortWrite(); } if (showLogOnError) { - showAndClearErrorLog(SaverState.SAVE); + showErrorLog(SaverState.SAVE, ctx); } } + private static class StackTraceTreeNode { + private final List records = new ArrayList<>(); + private final HashMap children = new HashMap<>(); + private final StackTraceTreeNode parent; - public void logError(String message, SaverState state, Throwable err) { - errorLog.add(new LoggedSavesError(message, state, err)); - } - - public void logError(String message, Throwable err) { - errorLog.add(new LoggedSavesError(message, state, err)); - } + public StackTraceTreeNode(StackTraceTreeNode parent) { + this.parent = parent; + } - public void clearErrorLog() { - errorLog.clear(); - } + private void addRecordToRoot(SerializationContext.ErrorRecord record) { + // add record to the tree + Throwable err = record.getException(); + if (err == null) { + records.add(record); + return; + } + // get original cause + while (err.getCause() != null) { + err = err.getCause(); + } + StackTraceTreeNode node = this; + StackTraceElement[] trace = err.getStackTrace(); + for (int i = trace.length - 1; i >= 0; i--) { + final StackTraceTreeNode parent = node; + node = Java8BackComp.computeIfAbsent(node.children, trace[i], e -> new StackTraceTreeNode(parent)); + } + node.records.add(record); + } - public void showAndClearErrorLog(SaverState state) { - if (!errorLog.isEmpty()) { - StringBuilder log = new StringBuilder(); - for (LoggedSavesError err : errorLog) { - log.append(err.message).append("\n").append(DialogHelper.getFormattedStackTrace(err.error)).append("\n\n"); + void appendToLog(StringBuilder sb, String prefix) { + if (!records.isEmpty() || children.size() > 1) { + prefix += ". "; + } + if (!records.isEmpty()) { + sb.append(prefix).append("\n"); + for (SerializationContext.ErrorRecord r : records) { + sb.append(prefix); + sb.append(r.formatMsg(false, 0, 0)); + sb.append("\n"); + } + sb.append(prefix).append("\n"); + } + for (Map.Entry node : children.entrySet()) { + sb.append(prefix).append(node.getKey()).append('\n'); + node.getValue().appendToLog(sb, prefix); } - Logger.error(log.toString()); - DialogHelper.openFormattedDialog("Errors occurred while " + (state == SaverState.READ ? "reading" : "saving") + " data:\n\n" + log, "SOME " + (state == SaverState.READ ? "READING" : "SAVING") + " ERRORS OCCURRED"); } - clearErrorLog(); } - public List getErrorLog() { - return errorLog; - } + public void showErrorLog(SaverState state, SerializationContext ctx) { + if (!ctx.hasErrors()) return; + StackTraceTreeNode tree = new StackTraceTreeNode(null); + for (SerializationContext.ErrorRecord err : ctx.errorsList) { + tree.addRecordToRoot(err); + } + StringBuilder log = new StringBuilder(); + tree.appendToLog(log, ""); - public static void logErrorStatic(String message, Throwable err) { - WorldDataSaver saver = WorldDataSaverHandler.getInstance().getWorldDataSaver(); - if (saver != null) { - saver.logError(message, err); + Logger.error("SERIALIZATION ERRORS: \n\n" + log.toString()); + if (state == SaverState.SAVE) { + if (savingErrorsReportedAsDialog) { + UserDialog.toast("errors occurred during saving data, see log for details"); + return; + } + savingErrorsReportedAsDialog = true; } + DialogHelper.openFormattedDialog("Errors occurred while " + (state == SaverState.READ ? "reading" : "saving") + " data:\n\n" + log, "SOME " + (state == SaverState.READ ? "READING" : "SAVING") + " ERRORS OCCURRED"); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java index fe58071f3..fdb4df314 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataSaverHandler.java @@ -1,8 +1,7 @@ package com.zhekasmirnov.innercore.api.runtime.saver.world; -import com.reider745.InnerCoreServer; import com.zhekasmirnov.apparatus.adapter.innercore.EngineConfig; -import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.apparatus.adapter.innercore.UserDialog; import com.zhekasmirnov.innercore.api.NativeAPI; import com.zhekasmirnov.innercore.api.log.ICLog; import com.zhekasmirnov.innercore.utils.OperationTimeLogger; @@ -27,12 +26,10 @@ public static WorldDataSaverHandler getInstance() { private long previousAutoSave = 0; public void fetchParamsFromConfig() { - final int auto_save_period = InnerCoreServer.getAutoSavePeriod(); - setParams( - auto_save_period >= 40, - InnerCoreServer.canAutoSaveWorld(), - auto_save_period * 1000 + EngineConfig.getBoolean("background.auto_save", true), + EngineConfig.getBoolean("background.auto_save_world", true), + EngineConfig.getInt("background.auto_save_period", 30) * 1000 ); } @@ -136,6 +133,6 @@ private synchronized void saveAndReleaseSaver() { } private void reportUnexpectedStateError(String message) { - Logger.debug("UNEXPECTED WORLD SAVER STATE", message); + UserDialog.dialog("UNEXPECTED WORLD SAVER STATE", message); } } diff --git a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java index 69d0b2453..09b46a46e 100644 --- a/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java +++ b/src/main/java/com/zhekasmirnov/innercore/api/runtime/saver/world/WorldDataScopeRegistry.java @@ -1,8 +1,11 @@ package com.zhekasmirnov.innercore.api.runtime.saver.world; -import com.zhekasmirnov.horizon.runtime.logger.Logger; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import com.zhekasmirnov.innercore.api.runtime.Callback; +import org.innercore.icstd.serialization.IScopedReader; +import org.innercore.icstd.serialization.IScopedWriter; +import org.innercore.internal.serialization.JsonIO; +import org.innercore.icstd.serialization.SerializationContext; import org.json.JSONObject; import org.mozilla.javascript.ScriptableObject; @@ -38,16 +41,8 @@ public static WorldDataScopeRegistry getInstance() { public interface SaverScope { - void readJson(Object json) throws Exception; - Object saveAsJson() throws Exception; - } - - public interface SavesErrorHandler { - void handle(String name, Throwable error); - } - - public interface MissingScopeHandler { - void handle(String name, Object data); + void read(IScopedReader io, SerializationContext ctx); + void write(IScopedWriter io, SerializationContext ctx); } @@ -63,33 +58,39 @@ public void addScope(String name, SaverScope scope) { scopeMap.put(name, scope); } - public void readAllScopes(JSONObject json, SavesErrorHandler errorHandler, MissingScopeHandler missingScopeHandler) { + public void readAllScopes(JSONObject json, SerializationContext ctx, Map outMissingScopes) { for (Map.Entry entry : scopeMap.entrySet()) { String key = entry.getKey(); Object data = json.opt(key); - if(key.isEmpty()) continue; - Logger.debug("Loaded save "+key); + JsonIO.ScopedReader reader = null; try { - entry.getValue().readJson(data); + reader = new JsonIO.ScopedReader(data); + entry.getValue().read(reader, ctx); } catch (Throwable err) { - errorHandler.handle(key, err); + ctx.addError(err, data, "global saves scope failed to read: " + key); + } + if (reader != null) { + reader.forceEndScope(ctx); } } for (Iterator it = json.keys(); it.hasNext(); ) { String key = it.next(); if (!scopeMap.containsKey(key)) { - missingScopeHandler.handle(key, json.opt(key)); + outMissingScopes.put(key, json.opt(key)); } } } - public void saveAllScopes(JSONObject json, SavesErrorHandler handler) { + public void saveAllScopes(JSONObject json, SerializationContext ctx) { for (Map.Entry entry : scopeMap.entrySet()) { try { - json.put(entry.getKey(), entry.getValue().saveAsJson()); + JsonIO.ScopedWriter writer = new JsonIO.ScopedWriter(); + entry.getValue().write(writer, ctx); + writer.forceEndScope(ctx); + json.put(entry.getKey(), writer.getResult()); } catch (Throwable err) { - handler.handle(entry.getKey(), err); + ctx.addError(err, null, "global saves scope failed to read: " + entry.getKey()); } } } diff --git a/src/main/java/org/innercore/icstd/annotations/ArgOverload.java b/src/main/java/org/innercore/icstd/annotations/ArgOverload.java new file mode 100644 index 000000000..86d7eb426 --- /dev/null +++ b/src/main/java/org/innercore/icstd/annotations/ArgOverload.java @@ -0,0 +1,12 @@ +package org.innercore.icstd.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target({ElementType.PARAMETER}) +public @interface ArgOverload { + String value(); +} diff --git a/src/main/java/org/innercore/icstd/blockentity/BlockEntityBase.java b/src/main/java/org/innercore/icstd/blockentity/BlockEntityBase.java new file mode 100644 index 000000000..2f07ddca8 --- /dev/null +++ b/src/main/java/org/innercore/icstd/blockentity/BlockEntityBase.java @@ -0,0 +1,145 @@ +package org.innercore.icstd.blockentity; + +import org.innercore.icstd.inventory.ItemContainer; +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.common.ChunkPos; +import org.innercore.icstd.component.ContainerComponent; +import org.innercore.icstd.component.DestroyBlockListenerComponent; +import org.innercore.icstd.component.DestroyObjectComponent; +import org.innercore.icstd.component.InitObjectComponent; +import org.innercore.icstd.scene.CreateObjectComponent; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.scene.GameObjectHandle; +import org.innercore.icstd.scene.LoadableComponent; +import org.innercore.icstd.scene.PeriodicObjectCheckComponent; +import org.innercore.icstd.serialization.ISerializableObject; +import org.innercore.icstd.updatable.UpdatableComponent; +import org.innercore.icstd.updatable.UpdatableHandle; +import org.innercore.icstd.updatable.UpdatableScheduler; +import org.innercore.icstd.world.DestroyBlockContext; + +public class BlockEntityBase implements BlockEntityComponent, CreateObjectComponent, LoadableComponent, ISerializableObject, PeriodicObjectCheckComponent, DestroyBlockListenerComponent, DestroyObjectComponent, InitObjectComponent { + public final boolean isServerSide; + + protected GameObjectHandle objHandle; + protected UpdatableHandle updHandle; + protected BlockSource region; + protected BlockPos blockPos; + // protected NetworkEntity netEntity; + + protected final BlockEntityType type; + private boolean isLoaded = false; + + @Override + public String getSerializationTypeId() { + return type.serializationTypeId; + } + + protected BlockEntityBase(BlockEntityType type, boolean isServerSide) { + this.isServerSide = isServerSide; + this.type = type; + } + + protected BlockEntityBase(boolean isServerSide) { + this.isServerSide = isServerSide; + type = BlockEntityRegistry.lookupType(getClass()); + if (type == null) { + throw new NullPointerException("no block entity type registered for class " + getClass()); + } + } + + @Override + public void createObject(GameObjectHandle handle) { + objHandle = handle; + region = BlockEntityRegistry.initBlockEntity(this); + blockPos = handle.getBlockPos(); + if (blockPos == null) + throw new IllegalStateException("block pos is null for block entity " + this + " handle=" + handle); + } + + @Override + public void init() { + /* if (type.netEntityType != null) { + netEntity = new NetworkEntity(type.netEntityType); + } */ + } + + @Override + public void destroy() { + if (this instanceof ContainerComponent) { + ItemContainer container = ((ContainerComponent) this).getContainer(); + if (container != null) { + container.close(); + container.removeEntity(); + } + } + /* if (netEntity != null) { + netEntity.remove(); + netEntity = null; + } */ + } + + protected void doDestroy(DestroyBlockContext ctx) { + if (updHandle != null) { + updHandle.destroy(); + } + objHandle.destroyNow(); + } + + @Override + public void doPeriodicObjectCheck() { + int blockId = region.getBlockId(blockPos.x, blockPos.y, blockPos.z); + if (blockId != type.blockId && region.isChunkLoaded(new ChunkPos(blockPos))) { + doDestroy(new DestroyBlockContext()); + } + } + + @Override + public void onBlockDestroyed(BlockSource region, BlockPos pos, DestroyBlockContext ctx) { + doDestroy(ctx); + } + + @Override + public void load() { + isLoaded = true; + if (this instanceof UpdatableComponent && updHandle == null) { + updHandle = (isServerSide ? UpdatableScheduler.createOnServer((UpdatableComponent) this) : UpdatableScheduler.createOnClient((UpdatableComponent) this)); + updHandle.schedule(1); // tick once after load to ensure correct DT + } + } + + @Override + public void unload() { + isLoaded = false; + if (updHandle != null) { + updHandle.destroy(); + updHandle = null; + } + } + + @Override + public GameObjectHandle getObjectHandle() { + return objHandle; + } + + @Override + public BlockEntityType getBlockEntityType() { + return type; + } + + public BlockSource getRegion() { + return region; + } + + public BlockPos getBlockPos() { + return blockPos; + } + + public boolean isLoaded() { + return isLoaded; + } + + public BlockEntityType getType() { + return type; + } +} diff --git a/src/main/java/org/innercore/icstd/blockentity/BlockEntityComponent.java b/src/main/java/org/innercore/icstd/blockentity/BlockEntityComponent.java new file mode 100644 index 000000000..286278457 --- /dev/null +++ b/src/main/java/org/innercore/icstd/blockentity/BlockEntityComponent.java @@ -0,0 +1,8 @@ +package org.innercore.icstd.blockentity; + +import org.innercore.icstd.scene.GameObjectHandle; + +public interface BlockEntityComponent { + BlockEntityType getBlockEntityType(); + GameObjectHandle getObjectHandle(); +} diff --git a/src/main/java/org/innercore/icstd/blockentity/BlockEntityRegistry.java b/src/main/java/org/innercore/icstd/blockentity/BlockEntityRegistry.java new file mode 100644 index 000000000..3f8e9fb6b --- /dev/null +++ b/src/main/java/org/innercore/icstd/blockentity/BlockEntityRegistry.java @@ -0,0 +1,137 @@ +package org.innercore.icstd.blockentity; + +import com.zhekasmirnov.apparatus.multiplayer.util.entity.NetworkEntityType; +import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.BlockEntityRegistryBindGenBase; +import com.zhekasmirnov.innercore.api.log.DialogHelper; +import com.zhekasmirnov.innercore.api.log.ICLog; +import org.innercore.icstd.blockentity.script.ScriptBlockEntity; +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.component.ComponentUtils; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.scene.GameObjectHandle; +import org.innercore.icstd.scene.GameObjectManager; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.ScriptableObject; + +import java.util.HashMap; + +@ApiBinding +public class BlockEntityRegistry extends BlockEntityRegistryBindGenBase { + private static final HashMap lookupByTypeName = new HashMap<>(); + private static final HashMap, BlockEntityType> lookupByClass = new HashMap<>(); + private static final HashMap lookupByBlockId = new HashMap<>(); + + public static BlockEntityType lookupType(Class cls) { + return lookupByClass.get(cls); + } + + public static BlockEntityType lookupType(String typeName) { + return lookupByTypeName.get(typeName); + } + + public static BlockEntityType lookupType(int blockId) { + return lookupByBlockId.get(blockId); + } + + public static BlockEntityType registerTypeNoInit(String typeName, Class typeClass, int blockId, IBlockEntityFactory factory, boolean registerLookupByClass, boolean canFail, boolean silent) { + if (lookupByTypeName.containsKey(typeName)) { + String msg = "duplicate block entity factory registered for typeName=" + typeName + " existing=" + lookupByTypeName.get(typeName).factory + " new=" + factory; + if (!silent) ICLog.e("BlockEntityRegistry", msg); + if (!canFail) throw new IllegalArgumentException(msg); + return null; + } + if (registerLookupByClass && lookupByClass.containsKey(typeClass)) { + String msg = "duplicate block entity factory registered for typeClass=" + typeClass + " existing=" + lookupByClass.get(typeClass).factory + " new=" + factory; + if (!silent) ICLog.e("BlockEntityRegistry", msg); + if (!canFail) throw new IllegalArgumentException(msg); + return null; + } + if (lookupByBlockId.containsKey(blockId)) { + String msg = "duplicate block entity factory registered for blockId=" + blockId + " existing=" + lookupByBlockId.get(blockId).factory + " new=" + factory; + if (!silent) ICLog.e("BlockEntityRegistry", msg); + if (!canFail) throw new IllegalArgumentException(msg); + return null; + } + BlockEntityType rec = new BlockEntityType(); + rec.typeName = typeName; + rec.typeClass = typeClass; + rec.blockId = blockId; + rec.factory = factory; + lookupByTypeName.put(typeName, rec); + if (registerLookupByClass) lookupByClass.put(typeClass, rec); + lookupByBlockId.put(blockId, rec); + return rec; + } + + public static BlockEntityType registerType(String typeName, Class cls, int blockId, IBlockEntityFactory factory, boolean registerLookupByClass, boolean canFail, boolean silent) { + BlockEntityType rec = registerTypeNoInit(typeName, cls, blockId, factory, registerLookupByClass, canFail, silent); + if (rec == null) + return null; + rec.initForClass(); + return rec; + } + + public static BlockEntityType registerType(String typeName, Class cls, int blockId, IBlockEntityFactory factory) { + return registerType(typeName, cls, blockId, factory, true, true, false); + } + + @ApiBinding("registerType") + public static boolean registerTypeForScript(int blockId, String typeName, String serializationTypeId, NetworkEntityType netEntityType, Function factory) { + BlockEntityType type = registerTypeNoInit(typeName, ScriptBlockEntity.class, blockId, () -> { + Object scriptObject = factory.call(Compiler.assureContextForCurrentThread(), factory.getParentScope(), factory.getParentScope(), new Object[0]); + if (!(scriptObject instanceof ScriptableObject)) { + throw new IllegalArgumentException("BlockEntityRegistry.registerScriptType: factory didn't return an object, got " + scriptObject); + } + return new ScriptBlockEntity(lookupType(typeName), (ScriptableObject) scriptObject, true); + }, /* lookupByClass */ false, /* canFail */ true, /* silent */ false); + if (type == null) + return false; + type.serializationTypeId = serializationTypeId; + type.netEntityType = netEntityType; + type.initForClass(); + return true; + } + + @ApiBinding + public static void addExistingScriptBlockEntityToServerScene(String typeName, ScriptableObject scriptObj, int dimensionId, int x, int y, int z) { + ScriptBlockEntity blockEntity = new ScriptBlockEntity(lookupType(typeName), scriptObj, true); + GameObjectHandle handle = GameObjectManager.getForDimension(dimensionId).create(blockEntity).placeAtBlockPos(x, y, z); + ComponentUtils.safeCallCreateObjectFor(blockEntity, handle); + } + + public static BlockEntityComponent createForBlock(int blockId) { + BlockEntityType rec = lookupByBlockId.get(blockId); + if (rec == null) + return null; + try { + return rec.factory.createBlockEntity(); + } catch (Throwable err) { + DialogHelper.reportNonFatalError("block entity factory " + rec.factory + " for blockId=" + blockId + " failed with exception", err); + return null; + } + } + + public static BlockEntityComponent createForBlockAndPlaceAtScene(GameObjectManager scene, int blockId, BlockPos pos) { + BlockEntityComponent blockEntity = createForBlock(blockId); + if (blockEntity == null) { + return null; + } + GameObjectHandle handle = scene.create(blockEntity).placeAtBlockPos(pos); + ComponentUtils.safeCallCreateObjectFor(blockEntity, handle); + return blockEntity; + } + + public static BlockSource initBlockEntity(BlockEntityComponent blockEntity) { + GameObjectManager scene = blockEntity.getObjectHandle().getScene(); + if (scene == null || scene.isDefaultScene()) { + throw new IllegalStateException("invalid scene " + scene + " for block entity " + blockEntity); + } + if (scene.isServerSide()) { + return BlockSource.getDefaultForDimension(scene.getDimensionId()); + } else { + return BlockSource.getCurrentClientRegion(); + } + } +} diff --git a/src/main/java/org/innercore/icstd/blockentity/BlockEntityType.java b/src/main/java/org/innercore/icstd/blockentity/BlockEntityType.java new file mode 100644 index 000000000..2e6a455a2 --- /dev/null +++ b/src/main/java/org/innercore/icstd/blockentity/BlockEntityType.java @@ -0,0 +1,65 @@ +package org.innercore.icstd.blockentity; + +import com.zhekasmirnov.apparatus.multiplayer.util.entity.NetworkEntityType; +import org.innercore.icstd.serialization.*; + +public class BlockEntityType { + String typeName; + Class typeClass; + IBlockEntityFactory factory; + int blockId; + String serializationTypeId; + NetworkEntityType netEntityType; + + private boolean isInitialized = false; + + public BlockEntityType initForClass() { + if (isInitialized) { + throw new IllegalStateException("BlockEntityType is already initialized"); + } + isInitialized = true; + if (SerializationMethodsComponent.class.isAssignableFrom(typeClass)) { + if (serializationTypeId == null) { + serializationTypeId = "b_ent:" + typeName; + } + serializationTypeId = ObjectSerializer.registerType(serializationTypeId, (Class) typeClass, new IObjectSerializer() { + @Override + public Object read(IScopedReader reader, SerializationContext ctx, String typeId) { + Object blockEntity = factory.createBlockEntity(); + ((SerializationMethodsComponent) blockEntity).read(reader, ctx, typeId); + return blockEntity; + } + + @Override + public void write(IScopedWriter writer, Object object, SerializationContext ctx, String typeId) { + ((SerializationMethodsComponent) object).write(writer, ctx, typeId); + } + }); + } + return this; + } + + public String getTypeName() { + return typeName; + } + + public Class getTypeClass() { + return typeClass; + } + + public NetworkEntityType getNetEntityType() { + return netEntityType; + } + + public String getSerializationTypeId() { + return serializationTypeId; + } + + public IBlockEntityFactory getFactory() { + return factory; + } + + public int getBlockId() { + return blockId; + } +} diff --git a/src/main/java/org/innercore/icstd/blockentity/IBlockEntityFactory.java b/src/main/java/org/innercore/icstd/blockentity/IBlockEntityFactory.java new file mode 100644 index 000000000..d21e7e993 --- /dev/null +++ b/src/main/java/org/innercore/icstd/blockentity/IBlockEntityFactory.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.blockentity; + +public interface IBlockEntityFactory { + T createBlockEntity(); +} diff --git a/src/main/java/org/innercore/icstd/blockentity/script/ScriptBlockEntity.java b/src/main/java/org/innercore/icstd/blockentity/script/ScriptBlockEntity.java new file mode 100644 index 000000000..f2c7fd6b2 --- /dev/null +++ b/src/main/java/org/innercore/icstd/blockentity/script/ScriptBlockEntity.java @@ -0,0 +1,161 @@ +package org.innercore.icstd.blockentity.script; + +import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.innercore.icstd.inventory.ItemContainer; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import com.zhekasmirnov.innercore.api.runtime.Callback; +import org.innercore.icstd.blockentity.BlockEntityBase; +import org.innercore.icstd.blockentity.BlockEntityType; +import org.innercore.icstd.common.BlockPosAndSide; +import org.innercore.icstd.common.ChunkPos; +import org.innercore.icstd.common.Vec3f; +import org.innercore.icstd.common.script.GenericScriptPos3; +import org.innercore.icstd.component.ContainerComponent; +import org.innercore.icstd.component.DiscardObjectComponent; +import org.innercore.icstd.component.ItemUseListenerComponent; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.scene.FinalizeObjectComponent; +import org.innercore.icstd.scene.GameObjectHandle; +import org.innercore.icstd.script.ScriptObjectComponent; +import org.innercore.icstd.serialization.*; +import org.innercore.icstd.updatable.UpdatableComponent; +import org.innercore.icstd.world.DestroyBlockContext; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +public class ScriptBlockEntity extends BlockEntityBase implements SerializationMethodsComponent, UpdatableComponent, ContainerComponent, ItemUseListenerComponent, ScriptObjectComponent, FinalizeObjectComponent, DiscardObjectComponent { + private final ScriptableObject scriptObject; + private final Scriptable parentScope; + + private static final Object[] NO_ARGS = new Object[0]; + private final Object[] ONE_ARG = new Object[1]; + + public ScriptBlockEntity(BlockEntityType blockEntityType, ScriptableObject scriptObject, boolean isServerSide) { + super(blockEntityType, isServerSide); + this.scriptObject = scriptObject; + parentScope = scriptObject.getParentScope(); + } + + @Override + public void createObject(GameObjectHandle handle) { + super.createObject(handle); + scriptObject.put("_objHandle", scriptObject, objHandle); + scriptObject.put("blockSource", scriptObject, region); + callMethod("_createdImpl", NO_ARGS); + // this is a legacy callback only for script block entities, it is used to do some setup and + // should be called as part of creation before init + Callback.invokeAPICallback("TileEntityAdded", scriptObject, true); + } + + @Override + public void finalizeObject() { + callMethod("finalized", NO_ARGS); + // this is a legacy callback only for script block entities, it is meant as finalization + Callback.invokeAPICallback("TileEntityRemoved", scriptObject); + } + + @Override + public void load() { + super.load(); + updHandle.schedule(1).setRecurringSchedule(1); + scriptObject.put("isLoaded", scriptObject, true); + scriptObject.put("_handle", scriptObject, updHandle); + callMethod("_loadImpl", NO_ARGS); + } + + @Override + public void unload() { + super.unload(); + scriptObject.put("isLoaded", scriptObject, false); + callMethod("unload", NO_ARGS); + } + + @Override + public void update(int dt) { + ONE_ARG[0] = dt; + callMethod("update", ONE_ARG); + } + + private boolean isDestroyCalled = false; + + @Override + protected void doDestroy(DestroyBlockContext ctx) { + ONE_ARG[0] = ctx; // FIXME: DestroyBlockContext must use codegen + if (!isDestroyCalled) + callMethod("_destroyImpl", new Object[]{ctx, true}); + isDestroyCalled = true; + super.doDestroy(ctx); + } + + @Override + public void destroy() { + if (!isDestroyCalled) + callMethod("_destroyImpl", new Object[]{null, false}); + isDestroyCalled = true; + super.destroy(); + } + + @Override + public void discard() { + callMethod("discard", NO_ARGS); + } + + private Object callMethod(String name, Object[] args) { + Object fn = scriptObject.get(name, scriptObject); + if (fn instanceof Function) { + return ((Function) fn).call(Compiler.assureContextForCurrentThread(), parentScope, scriptObject, args); + } + return null; + } + + @Override + public String getSerializationTypeId() { + return ObjectSerializer.getSerializerTypeIdFromObject(scriptObject); + } + + @Override + public ItemContainer getContainer() { + Object o = scriptObject.get("container", scriptObject); + if (o instanceof ItemContainer) { + // FIXME: this won't work until ItemContainer is transferred to codegen + return (ItemContainer) o; + } + return null; + } + + @Override + public void doPeriodicObjectCheck() { + super.doPeriodicObjectCheck(); + int blockId = region.getBlockId(blockPos.x, blockPos.y, blockPos.z); + if (blockId != type.getBlockId() && region.isChunkLoaded(new ChunkPos(blockPos))) { + // scripts can override it + int typeBlockId = ScriptableObjectHelper.getIntProperty(scriptObject, "blockID", type.getBlockId()); + if (blockId != typeBlockId) { + doDestroy(new DestroyBlockContext()); + } + } + // onCheckerTick in script tile entities is never used, so don't try to call it + } + + @Override + public void onItemUse(BlockSource source, BlockPosAndSide pos, ItemInstance item, long playerUid, Vec3f exactPos) { + callMethod("onItemClick", new Object[]{item.id, item.count, item.data, new GenericScriptPos3(pos), playerUid, item.extra}); + } + + @Override + public void read(IScopedReader io, SerializationContext ctx, String typeId) { + callMethod("_readImpl", new Object[]{ ScriptableObjectSerializer.readScriptable(io, ctx) }); + } + + @Override + public void write(IScopedWriter io, SerializationContext ctx, String typeId) { + ScriptableObjectSerializer.writeScriptable(io, callMethod("_writeImpl", NO_ARGS), ctx); + } + + @Override + public Object getScriptObject() { + return scriptObject; + } +} diff --git a/src/main/java/org/innercore/icstd/common/script/GenericScriptPos3.java b/src/main/java/org/innercore/icstd/common/script/GenericScriptPos3.java index 4ee7b4d40..244eaf086 100644 --- a/src/main/java/org/innercore/icstd/common/script/GenericScriptPos3.java +++ b/src/main/java/org/innercore/icstd/common/script/GenericScriptPos3.java @@ -7,6 +7,7 @@ import org.innercore.icstd.common.BlockPosAndSide; import org.innercore.icstd.common.Vec3f; import org.innercore.icstd.common.Vec3i; +import org.jetbrains.annotations.Nullable; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; @@ -17,8 +18,10 @@ public class GenericScriptPos3 extends GenericPos3BindGenBase { @ApiBinding public double z; @ApiBinding public int side; - @ApiBinding public GenericScriptPos3 vec = null; - @ApiBinding public GenericScriptPos3 relative = null; + @ApiBinding @Nullable + public GenericScriptPos3 vec = null; + @ApiBinding @Nullable + public GenericScriptPos3 relative = null; @ApiBinding public GenericScriptPos3(double x, double y, double z, int side) { @@ -90,11 +93,10 @@ public static GenericScriptPos3 makeWithSideAndRelative(double x, double y, doub @ApiBinding public static GenericScriptPos3 parse(Object o, boolean nullable) { - if (o instanceof GenericScriptPos3) { - return (GenericScriptPos3) o; + if (o instanceof GenericScriptPos3 s) { + return s; } - if (o instanceof Scriptable) { - Scriptable s = (Scriptable) o; + if (o instanceof Scriptable s) { double x = ScriptableObjectHelper.getDoubleProperty(s, "x", 0); double y = ScriptableObjectHelper.getDoubleProperty(s, "y", 0); double z = ScriptableObjectHelper.getDoubleProperty(s, "z", 0); diff --git a/src/main/java/org/innercore/icstd/component/ComponentUtils.java b/src/main/java/org/innercore/icstd/component/ComponentUtils.java new file mode 100644 index 000000000..51186acd6 --- /dev/null +++ b/src/main/java/org/innercore/icstd/component/ComponentUtils.java @@ -0,0 +1,115 @@ +package org.innercore.icstd.component; + +import com.zhekasmirnov.innercore.api.log.DialogHelper; +import com.zhekasmirnov.innercore.api.log.ICLog; +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.scene.*; +import org.innercore.icstd.world.DestroyBlockContext; + +public class ComponentUtils { + public static void reportComponentCallError(Object o, Throwable err, String method) { + String msg = o + "." + method + "() failed with exception"; + DialogHelper.reportNonFatalError(msg, err); + ICLog.e("ComponentUtils", msg, err); + } + + public static boolean safeCallCreateObjectFor(Object o, GameObjectHandle handle) { + if (o instanceof CreateObjectComponent) { + try { + ((CreateObjectComponent) o).createObject(handle); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, "createObject"); + } + } + return false; + } + + public static boolean safeCallInitFor(Object o) { + if (o instanceof InitObjectComponent) { + try { + ((InitObjectComponent) o).init(); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, "init"); + } + } + return false; + } + + public static boolean safeCallDestroyFor(Object o) { + if (o instanceof DestroyObjectComponent) { + try { + ((DestroyObjectComponent) o).destroy(); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, "destroy"); + } + } + return false; + } + + public static boolean safeCallBlockDestroyedFor(Object o, BlockSource region, BlockPos pos, DestroyBlockContext ctx) { + if (o instanceof DestroyBlockListenerComponent) { + try { + ((DestroyBlockListenerComponent) o).onBlockDestroyed(region, pos, ctx); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, "onBlockDestroyed"); + } + } + return false; + } + + public static boolean safeCallDiscardFor(Object o) { + if (o instanceof DiscardObjectComponent) { + try { + ((DiscardObjectComponent) o).discard(); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, "discard"); + } + } + return false; + } + + public static boolean safeCallFinalizeObjectFor(Object o) { + if (o instanceof FinalizeObjectComponent) { + try { + ((FinalizeObjectComponent) o).finalizeObject(); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, "finalizeObject"); + } + } + return false; + } + + public static boolean safeCallLoadOrUnloadFor(Object o, boolean isLoaded) { + if (o instanceof LoadableComponent) { + try { + if (isLoaded) + ((LoadableComponent) o).load(); + else + ((LoadableComponent) o).unload(); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, isLoaded ? "load" : "unload"); + } + } + return false; + } + + public static boolean safeCallPeriodicCheckFor(Object o) { + if (o instanceof PeriodicObjectCheckComponent) { + try { + ((PeriodicObjectCheckComponent) o).doPeriodicObjectCheck(); + return true; + } catch (Throwable err) { + ComponentUtils.reportComponentCallError(o, err, "doPeriodicObjectCheck"); + } + } + return false; + } +} diff --git a/src/main/java/org/innercore/icstd/component/ContainerComponent.java b/src/main/java/org/innercore/icstd/component/ContainerComponent.java new file mode 100644 index 000000000..73bb02d7b --- /dev/null +++ b/src/main/java/org/innercore/icstd/component/ContainerComponent.java @@ -0,0 +1,7 @@ +package org.innercore.icstd.component; + +import org.innercore.icstd.inventory.ItemContainer; + +public interface ContainerComponent { + ItemContainer getContainer(); +} diff --git a/src/main/java/org/innercore/icstd/component/DestroyBlockListenerComponent.java b/src/main/java/org/innercore/icstd/component/DestroyBlockListenerComponent.java new file mode 100644 index 000000000..0c1f44e8c --- /dev/null +++ b/src/main/java/org/innercore/icstd/component/DestroyBlockListenerComponent.java @@ -0,0 +1,9 @@ +package org.innercore.icstd.component; + +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.world.DestroyBlockContext; + +public interface DestroyBlockListenerComponent { + void onBlockDestroyed(BlockSource region, BlockPos pos, DestroyBlockContext ctx); +} diff --git a/src/main/java/org/innercore/icstd/component/DestroyObjectComponent.java b/src/main/java/org/innercore/icstd/component/DestroyObjectComponent.java new file mode 100644 index 000000000..132cdb4c9 --- /dev/null +++ b/src/main/java/org/innercore/icstd/component/DestroyObjectComponent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.component; + +public interface DestroyObjectComponent { + void destroy(); +} diff --git a/src/main/java/org/innercore/icstd/component/DiscardObjectComponent.java b/src/main/java/org/innercore/icstd/component/DiscardObjectComponent.java new file mode 100644 index 000000000..27c9ca50c --- /dev/null +++ b/src/main/java/org/innercore/icstd/component/DiscardObjectComponent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.component; + +public interface DiscardObjectComponent { + void discard(); +} diff --git a/src/main/java/org/innercore/icstd/component/InitObjectComponent.java b/src/main/java/org/innercore/icstd/component/InitObjectComponent.java new file mode 100644 index 000000000..ae6643e68 --- /dev/null +++ b/src/main/java/org/innercore/icstd/component/InitObjectComponent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.component; + +public interface InitObjectComponent { + void init(); +} diff --git a/src/main/java/org/innercore/icstd/component/ItemUseListenerComponent.java b/src/main/java/org/innercore/icstd/component/ItemUseListenerComponent.java new file mode 100644 index 000000000..767ceb57d --- /dev/null +++ b/src/main/java/org/innercore/icstd/component/ItemUseListenerComponent.java @@ -0,0 +1,10 @@ +package org.innercore.icstd.component; + +import org.innercore.icstd.common.BlockPosAndSide; +import org.innercore.icstd.common.Vec3f; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.region.BlockSource; + +public interface ItemUseListenerComponent { + void onItemUse(BlockSource source, BlockPosAndSide pos, ItemInstance item, long playerUid, Vec3f exactPos); +} diff --git a/src/main/java/org/innercore/icstd/debug/SamplingProfiler.java b/src/main/java/org/innercore/icstd/debug/SamplingProfiler.java new file mode 100644 index 000000000..6b9ac4bfa --- /dev/null +++ b/src/main/java/org/innercore/icstd/debug/SamplingProfiler.java @@ -0,0 +1,303 @@ +package org.innercore.icstd.debug; + +import com.zhekasmirnov.innercore.api.gen.SamplingProfilerBindGenBase; +import com.zhekasmirnov.innercore.api.log.ICLog; +import com.zhekasmirnov.innercore.utils.FileTools; +import org.innercore.icstd.annotations.ApiBinding; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +@ApiBinding +public class SamplingProfiler extends SamplingProfilerBindGenBase { + public static class Sample { + StackTraceElement[] trace; + long time; + + public Sample(StackTraceElement[] trace, long time) { + this.trace = trace; + this.time = time; + } + } + + private static class RecordedThread { + Thread thread; + String baseFrameName; + private List samples = new ArrayList<>(); + } + private final ArrayList recordedThreads = new ArrayList<>(); + + private Thread samplingThread = null; + private long samplingDelayNs = 1_000_000; + + @ApiBinding + public SamplingProfiler() { + } + + private volatile boolean active = false; + private volatile boolean recording = false; + private final Object startRecEvent = new Object(); + + @ApiBinding + public void addCurrentThread(String name) { + Thread thread = Thread.currentThread(); + RecordedThread rec = null; + for (RecordedThread recordedThread : recordedThreads) { + if (recordedThread.thread == thread) { + rec = recordedThread; + break; + } + } + if (rec == null) { + rec = new RecordedThread(); + rec.thread = thread; + recordedThreads.add(rec); + } + rec.baseFrameName = name; + } + + @ApiBinding + public void setSamplingDelayNs(long samplingDelayNs) { + this.samplingDelayNs = samplingDelayNs; + } + + @ApiBinding + public void setSamplingRate(int samplesPerSec) { + setSamplingDelayNs((long) 1_000_000_000 / (long) samplesPerSec); + } + + @ApiBinding + public void setActive(boolean newActive) { + if (recordedThreads.isEmpty() && newActive) { + throw new IllegalStateException("no recorded threads"); + } + if (this.active == newActive) { + return; + } + this.active = newActive; + if (active) { + for (RecordedThread rt : recordedThreads) { + rt.samples.clear(); + } + samplingThread = new Thread(() -> { + while (active) { + while (!recording) { + try { + synchronized (startRecEvent) { + startRecEvent.wait(); + } + } catch (InterruptedException e) { + return; + } + } + long nextSampleTime = System.nanoTime(); + while (recording) { + while (System.nanoTime() < nextSampleTime) { + Thread.yield(); + } + nextSampleTime += samplingDelayNs; + for (RecordedThread rt : recordedThreads) { + StackTraceElement[] trace = rt.thread.getStackTrace(); + long sampleTime = System.nanoTime(); + rt.samples.add(new Sample(trace, sampleTime)); + } + } + } + }); + samplingThread.start(); + } else { + try { + this.recording = false; + samplingThread.join(); + samplingThread = null; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + @ApiBinding + public void setRecording(boolean recording) { + if (this.recording == recording) { + return; + } + this.recording = recording; + if (recording) { + synchronized (startRecEvent) { + startRecEvent.notify(); + } + } + } + + @ApiBinding + public void activateAndRecord() { + setActive(true); + setRecording(true); + } + + @ApiBinding + public void stopAndDeactivate() { + setActive(false); + } + + private boolean cfgSkipEmptySamples = true; + private boolean cfgFilterFrames = true; + private boolean cfgGroupFunctions = true; + private boolean cfgShortenPackageNames = true; + private boolean cfgAnnotateRootTime = true; + + @ApiBinding + public void setCfgSkipEmptySamples(boolean cfgSkipEmptySamples) { + this.cfgSkipEmptySamples = cfgSkipEmptySamples; + } + + @ApiBinding + public void setCfgFilterFrames(boolean cfgFilterFrames) { + this.cfgFilterFrames = cfgFilterFrames; + } + + @ApiBinding + public void setCfgGroupFunctions(boolean cfgGroupFunctions) { + this.cfgGroupFunctions = cfgGroupFunctions; + } + + @ApiBinding + public void setCfgShortenPackageNames(boolean cfgShortenPackageNames) { + this.cfgShortenPackageNames = cfgShortenPackageNames; + } + + @ApiBinding + public void setCfgAnnotateRootTime(boolean cfgAnnotateRootTime) { + this.cfgAnnotateRootTime = cfgAnnotateRootTime; + } + + private String calcAndFormatThreadStats(List samples) { + long last = !samples.isEmpty() ? samples.get(0).time - samplingDelayNs : 0; + long activeTime = 0; + long lastActiveTime = 0; + long tickCount = 0; + for (Sample sample : samples) { + if (sample.time < last) { + continue; // skip broken samples + } + StackTraceElement[] trace = sample.trace; + if (trace.length == 0 && cfgSkipEmptySamples) { + last = sample.time; + continue; + } + if (sample.time - lastActiveTime > samplingDelayNs * 5) { + tickCount++; + } + activeTime += sample.time - last; + last = lastActiveTime = sample.time; + } + long totalTime = !samples.isEmpty() ? samples.get(samples.size() - 1).time - samples.get(0).time : 0; + return String.format("total:%dms,tick:%.3fms(%d),active:%.1f%%", + totalTime / 1_000_000, + ((activeTime) / tickCount / 1000) * 0.001, + tickCount, + ((activeTime * 1000) / totalTime) * 0.1 + ); + } + + @ApiBinding + public String toStatInfoStr() { + StringBuilder sb = new StringBuilder(); + for (RecordedThread rt : recordedThreads) { + sb.append(rt.baseFrameName).append(": ").append(calcAndFormatThreadStats(rt.samples)).append("\n"); + } + return sb.toString(); + } + + @ApiBinding + public String toFoldedStacksStr() { + if (active) { + throw new IllegalStateException("setActive(false) must be called before export"); + } + HashMap map = new HashMap<>(); + + for (RecordedThread rt : recordedThreads) { + String baseFrame = "root;" + rt.baseFrameName; + List samples = rt.samples; + + if (cfgAnnotateRootTime) { + baseFrame += "(" + calcAndFormatThreadStats(samples) + ")"; + } + + final String SHORTEN_PACKAGE_SKIP_1 = "com.zhekasmirnov.innercore.api."; + final String SHORTEN_PACKAGE_SKIP_2 = "org.innercore.icstd."; + + long lastSampleTime = !samples.isEmpty() ? samples.get(0).time - samplingDelayNs : 0; + for (Sample sample : samples) { + if (sample.time < lastSampleTime) { + continue; // skip broken samples + } + StackTraceElement[] trace = sample.trace; + if (trace.length == 0 && cfgSkipEmptySamples) { + lastSampleTime = sample.time; + continue; + } + StringBuilder sb = new StringBuilder(baseFrame); + for (int i = trace.length - 1; i >= 0; i--) { + StackTraceElement traceElement = trace[i]; + String clsName = traceElement.getClassName(); + boolean isJsGenFrame = clsName.startsWith("org.mozilla.javascript.gen") || clsName.startsWith("js.opt."); + boolean isInternalJsFrame = !isJsGenFrame && clsName.startsWith("org.mozilla.javascript"); + boolean isInternalApiFrame = clsName.contains("BindGenBase"); + boolean isScriptFrame = isJsGenFrame && traceElement.getLineNumber() > 0 && traceElement.getFileName() != null; + if (cfgFilterFrames) { + if (isJsGenFrame && !isScriptFrame || isInternalApiFrame || isInternalJsFrame) + continue; + } + if (isScriptFrame) { + String methodName = traceElement.getMethodName(); + if (cfgShortenPackageNames && methodName.startsWith("_c_")) + methodName = methodName.substring(3); + if (cfgGroupFunctions) + sb.append(';').append(traceElement.getFileName()).append('.').append(methodName); + sb.append(';').append(traceElement.getFileName()).append(":").append(traceElement.getLineNumber()); + if (!cfgGroupFunctions) + sb.append(" (").append(methodName).append(")"); + } else { + String frameStr = traceElement.toString(); + if (cfgShortenPackageNames) { + if (frameStr.startsWith(SHORTEN_PACKAGE_SKIP_1)) + frameStr = frameStr.substring(SHORTEN_PACKAGE_SKIP_1.length()); + else if (frameStr.startsWith(SHORTEN_PACKAGE_SKIP_2)) + frameStr = frameStr.substring(SHORTEN_PACKAGE_SKIP_2.length()); + } + int openingBracketIdx = frameStr.indexOf('('); + if (cfgGroupFunctions && openingBracketIdx != -1) { + sb.append(';').append(frameStr, 0, openingBracketIdx); + } else { + sb.append(';').append(frameStr); + } + } + } + map.put(sb.toString(), map.getOrDefault(sb.toString(), 0L) + (sample.time - lastSampleTime)); + lastSampleTime = sample.time; + } + } + StringBuilder sb = new StringBuilder(); + map.forEach((k, v) -> { + sb.append(k).append(' ').append((v + samplingDelayNs / 2) / samplingDelayNs).append('\n'); + }); + return sb.toString(); + } + + @ApiBinding + public static void writeStrDataToDefaultPerfFile(String data, String name) { + try { + FileTools.writeFileText(FileTools.DIR_PACK + "perf.txt", data); + } catch (IOException e) { + ICLog.e("SamplingProfiler", "Error writing perf file: " + e.getMessage()); + } + } + + @ApiBinding + public static void writeStrDataToDefaultPerfFile(String data) { + writeStrDataToDefaultPerfFile(data, "perf.txt"); + } +} diff --git a/src/main/java/org/innercore/icstd/entity/PathNavigation.java b/src/main/java/org/innercore/icstd/entity/PathNavigation.java new file mode 100644 index 000000000..bf8b0497f --- /dev/null +++ b/src/main/java/org/innercore/icstd/entity/PathNavigation.java @@ -0,0 +1,327 @@ +package org.innercore.icstd.entity; + +import com.reider745.InnerCoreServer; +import com.zhekasmirnov.innercore.api.gen.PathNavigationBindGenBase; +import org.innercore.icstd.annotations.ApiBinding; + +import java.util.HashMap; + +@ApiBinding("PathNavigation") +public class PathNavigation extends PathNavigationBindGenBase { + private static HashMap activeNavigationMap = new HashMap<>(); + + private static native long nativeConstruct(long entity); + private static native void nativeCleanup(); + + private long entity; + + public PathNavigation(long entity) { + //super(entity); + this.entity = entity; + } + + public PathNavigation(long ptr, int refCnt, Object stub) { + //super(ptr, refCnt, stub); + } + + @ApiBinding + public static void cleanupAll() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.cleanupAll"); + } + + @ApiBinding + public void moveToCoords(float x, float y, float z, float dist) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.moveToCoords"); + } + + @ApiBinding + public void moveToEntity(long entity, float dis) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.moveToEntity"); + } + + @ApiBinding + public float getMaxNavigationDistance() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getMaxNavigationDistance"); + return 0; + } + + @ApiBinding + public void setMaxNavigationDistance(float value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setMaxNavigationDistance"); + } + + @ApiBinding + public boolean isDone() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.isDone"); + return true; + } + + @ApiBinding + public void stop() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.stop"); + } + + @ApiBinding + public void setType(int type) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setType"); + } + + @ApiBinding + public boolean canPassDoors() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.canPassDoors"); + return false; + } + + @ApiBinding + public void setCanPassDoors(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanPassDoors"); + } + + @ApiBinding + public boolean canOpenDoors() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.canOpenDoors"); + return false; + } + + @ApiBinding + public void setCanOpenDoors(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanOpenDoors"); + } + + @ApiBinding + public boolean isRiverFollowing() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.isRiverFollowing"); + return false; + } + + @ApiBinding + public void setIsRiverFollowing(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setIsRiverFollowing"); + } + + @ApiBinding + public float getEndPathRadiusSqr() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getEndPathRadiusSqr"); + return 0; + } + + @ApiBinding + public void setEndPathRadius(float value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setEndPathRadius"); + } + + @ApiBinding + public boolean getAvoidSun() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidSun"); + return false; + } + + @ApiBinding + public void setAvoidSun(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidSun"); + } + + @ApiBinding + public boolean getAvoidWater() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidWater"); + return false; + } + + @ApiBinding + public void setAvoidWater(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidWater"); + } + + @ApiBinding + public boolean getCanSink() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanSink"); + return false; + } + + @ApiBinding + public void setCanSink(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanSink"); + } + + @ApiBinding + public boolean getAvoidDamageBlocks() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidDamageBlocks"); + return false; + } + + @ApiBinding + public void setAvoidDamageBlocks(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidDamageBlocks"); + } + + @ApiBinding + public boolean getCanFloat() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanFloat"); + return false; + } + + @ApiBinding + public void setCanFloat(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanFloat"); + } + + @ApiBinding + public boolean getAvoidPortals() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getAvoidPortals"); + return false; + } + + @ApiBinding + public void setAvoidPortals(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setAvoidPortals"); + } + + @ApiBinding + public boolean getCanBreach() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanBreach"); + return false; + } + + @ApiBinding + public void setCanBreach(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanBreach"); + } + + @ApiBinding + public boolean getCanJump() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanJump"); + return false; + } + + @ApiBinding + public void setCanJump(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanJump"); + } + + @ApiBinding + public boolean getCanPathOverLava() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanPathOverLava"); + return false; + } + + @ApiBinding + public void setCanPathOverLava(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanPathOverLava"); + } + + @ApiBinding + public boolean getCanWalkInLava() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanWalkInLava"); + return false; + } + + @ApiBinding + public void setCanWalkInLava(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanWalkInLava"); + } + + @ApiBinding + public boolean getCanOpenIronDoors() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getCanOpenIronDoors"); + return false; + } + + @ApiBinding + public void setCanOpenIronDoors(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setCanOpenIronDoors"); + } + + @ApiBinding + public boolean getHasEndPathRadius() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getHasEndPathRadius"); + return false; + } + + @ApiBinding + public void setHasEndPathRadius(boolean value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setHasEndPathRadius"); + } + + @ApiBinding + public float getSpeed() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getSpeed"); + return 0.0f; + } + + @ApiBinding + public void setSpeed(float value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setSpeed"); + } + + @ApiBinding + public float getTerminationThreshold() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getTerminationThreshold"); + return 0.0f; + } + + @ApiBinding + public void setTerminationThreshold(float value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setTerminationThreshold"); + } + + @ApiBinding + public int getTickTimeout() { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.getTickTimeout"); + return 0; + } + + @ApiBinding + public void setTickTimeout(int value) { + InnerCoreServer.useNotCurrentSupport("NativePathNavigation.setTickTimeout"); + } + + + @Override + public String toString() { + return "[PathNavigation entity=" + entity + "]"; + } + + public static PathNavigation getNavigation(long entity) { + PathNavigation result = activeNavigationMap.get(entity); + if(result != null){ + return result; + } + result = new PathNavigation(entity); + activeNavigationMap.put(entity, result); + return result; + } + + public static void onNavigationResult(long entity, int result){ + PathNavigation navigation = getNavigation(entity); + if(navigation.resultFunction != null){ + navigation.resultFunction.onNavigationResult(navigation, result); + } + } + + public long getEntity() { + return entity; + } + + public static void cleanup() { + for(PathNavigation navigation: activeNavigationMap.values()) { + navigation.entity = -1; + } + activeNavigationMap.clear(); + cleanupAll(); + } + + public interface NavigationResultFunction { + void onNavigationResult(PathNavigation navigation, int result); + } + + PathNavigation.NavigationResultFunction resultFunction; + + @ApiBinding + public PathNavigation setResultFunction(PathNavigation.NavigationResultFunction resultFunction) { + if (this.resultFunction != null) { + this.resultFunction.onNavigationResult(this, 6); // FUNCTION_REPLACED + } + this.resultFunction = resultFunction; + return this; + } +} diff --git a/src/main/java/org/innercore/icstd/event/EventController.java b/src/main/java/org/innercore/icstd/event/EventController.java new file mode 100644 index 000000000..a06790139 --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/EventController.java @@ -0,0 +1,14 @@ +package org.innercore.icstd.event; + +import java.util.List; + +public class EventController { + public boolean catchExceptions = false; + public List errors = null; + + // used for caching mostly + public Object scriptCtx = null; + public Object scriptScope = null; + public Object scriptEvent = null; + public Object[] scriptArgs = null; +} diff --git a/src/main/java/org/innercore/icstd/event/Events.java b/src/main/java/org/innercore/icstd/event/Events.java new file mode 100644 index 000000000..80091064a --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/Events.java @@ -0,0 +1,229 @@ +package org.innercore.icstd.event; + +import java.util.*; + +public class Events { + private static class EventListenerData { + Object listener; + private int index; + + private int priority = 0; + + public EventListenerData(Object listener, int index) { + this.listener = listener; + this.index = index; + } + } + + private static class EventListenerRecord { + String name = null; + boolean isManaged = false; + boolean hasJavaListeners = false; + boolean hasScriptListeners = false; + List listeners = new ArrayList<>(); + List listenersData = new ArrayList<>(); + + EventBuilder addListener(Object listener) { + EventListenerData data = new EventListenerData(listener, listeners.size()); + listeners.add(listener); + listenersData.add(data); + isManaged = isManaged || listener instanceof IManagedEventListener; + // FIXME: + // isManaged = isManaged || (listener instanceof IScriptEventListener && ((IScriptEventListener) listener).isEventCtrlRequired()); + hasScriptListeners = hasScriptListeners || listener instanceof IScriptEventListener; + hasJavaListeners = hasJavaListeners || !(listener instanceof IScriptEventListener); + return new EventBuilder(this, listener, data); + } + + void onOrderChanged() { + listenersData.sort(Comparator.comparingInt(data -> data.priority)); + for (int i = 0; i < listenersData.size(); i++) { + EventListenerData data = listenersData.get(i); + data.index = i; + listeners.set(i, data.listener); + } + } + } + + private static final HashMap, EventListenerRecord> eventRecordByClass = new HashMap<>(); + private static final HashMap eventRecordByName = new HashMap<>(); + private static final HashMap> scriptEventClsByName = new HashMap<>(); + private static final HashMap> scriptEventConverters = new HashMap<>(); + + public static int count(String name) { + EventListenerRecord record = eventRecordByName.get(name); + if(record == null) return 0; + return record.listeners.size(); + } + + public static class EventBuilder { + final EventListenerRecord eventRecord; + final Object listener; + final EventListenerData data; + + EventBuilder(EventListenerRecord eventRecord, Object listener, EventListenerData data) { + this.eventRecord = eventRecord; + this.listener = listener; + this.data = data; + } + + public EventBuilder setPriority(int priority) { + this.data.priority = priority; + this.eventRecord.onOrderChanged(); + return this; + } + } + + private static String getAndValidateScriptEventClassName(Class eventClass) { + ScriptEventName ann = eventClass.getAnnotation(ScriptEventName.class); + if (ann == null || ann.value().isEmpty()) + throw new IllegalArgumentException("Event class " + eventClass + " must be annotated with @AnnotateEvent with non-empty name"); + return ann.value(); + } + + private static void reportClsNameConflict(String eventName, Class newCls, Class registeredCls) { + System.out.println("Found conflicting event classes for event name '" + eventName + "' " + registeredCls.getName() + " != " + newCls.getName() + " latter will be skipped"); + // todo: + } + + private static EventBuilder onEventImpl(Class eventCls, Object listener) { + EventListenerRecord rec = eventRecordByClass.get(eventCls); + if (rec == null) { + rec = new EventListenerRecord(); + // handle aliasing script events by name + if (IScriptEvent.class.isAssignableFrom(eventCls)) { + String name = getAndValidateScriptEventClassName(eventCls); + Class registeredCls = scriptEventClsByName.get(name); + if (registeredCls == null) { + try { + Class.forName(eventCls.getName(), true, eventCls.getClassLoader()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + eventRecordByName.put(name, rec); + scriptEventClsByName.put(name, eventCls); + } else if (registeredCls.equals(eventCls)) { + rec = eventRecordByName.get(name); + } else { + reportClsNameConflict(name, eventCls, registeredCls); + } + } + eventRecordByClass.put(eventCls, rec); + } + + return rec.addListener(listener); + } + + private static EventBuilder onEventImpl(String eventName, Object listener) { + EventListenerRecord rec = eventRecordByName.get(eventName); + if (rec == null) { + rec = new EventListenerRecord(); + eventRecordByName.put(eventName, rec); + } + return rec.addListener(listener); + } + + private static void invokeImpl(EventListenerRecord rec, Object event, EventController ctrl, Object[] scriptArgs) { + final boolean isManaged = rec.isManaged; + if (isManaged && ctrl == null) + ctrl = new EventController(); + final boolean catchExceptions = ctrl != null && ctrl.catchExceptions; + + if (event == null) { + // separate path to script-to-script events + for (Object listener : rec.listeners) { + if (listener instanceof IScriptEventListener) { + try { + ((IScriptEventListener) listener).onEvent(ctrl, scriptArgs); + } catch (Throwable t) { + if (catchExceptions) { + if (ctrl.errors == null) ctrl.errors = new ArrayList<>(); + ctrl.errors.add(t); + } else throw t; + } + } + } + return; + } + final boolean isScriptEvent = event instanceof IScriptEvent; + if ((!isScriptEvent && !rec.hasScriptListeners) && !isManaged) { + // fast path for unmanaged and not script events + for (Object listener : rec.listeners) { + try { + ((IEventListener) listener).onEvent(event); + } catch (Throwable t) { + if (catchExceptions) { + if (ctrl.errors == null) ctrl.errors = new ArrayList<>(); + ctrl.errors.add(t); + } else throw t; + } + } + return; + } + // slow path for mixed listeners + for (Object listener : rec.listeners) { + if (isManaged) { + // todo: + } + try { + if (listener instanceof IEventListener) { + ((IEventListener) listener).onEvent(event); + } else if (listener instanceof IManagedEventListener) { + ((IManagedEventListener) listener).onEvent(ctrl, event); + } else if (listener instanceof IScriptEventListener) { + if (isScriptEvent) { + if (scriptArgs == null) + scriptArgs = ((IScriptEvent) event).asScriptArgs(rec.name); + ((IScriptEventListener) listener).onEvent(ctrl, scriptArgs); + } + } + } catch (Throwable t) { + if (catchExceptions) { + if (ctrl.errors == null) ctrl.errors = new ArrayList<>(); + ctrl.errors.add(t); + } else throw t; + } + } + } + + // + + public static EventBuilder on(Class eventCls, IEventListener listener) { return onEventImpl(eventCls, listener); } + + public static EventBuilder on(Class eventCls, IManagedEventListener listener) { return onEventImpl(eventCls, listener); } + + public static EventBuilder onScriptEvent(String eventName, IScriptEventListener listener) { return onEventImpl(eventName, listener); } + + public static void invoke(EventController ctrl, T event) { + EventListenerRecord rec = eventRecordByClass.get(event.getClass()); + if (rec != null) + invokeImpl(rec, event, ctrl, null); + } + + public static void invoke(T event) { invoke(null, event);} + + public static void addScriptEventConverter(Class eventCls, IScriptEventConverter cvt) { + String name = getAndValidateScriptEventClassName(eventCls); + if (scriptEventClsByName.containsKey(name)) { + reportClsNameConflict(name, eventCls, scriptEventClsByName.get(name)); + return; + } + scriptEventConverters.put(name, cvt); + } + + public static void invokeScriptEvent(EventController ctrl, String name, Object[] scriptArgs) { + EventListenerRecord rec = eventRecordByName.get(name); + if (rec == null) + return; + Object event = null; + if (rec.hasJavaListeners) { + IScriptEventConverter converter = scriptEventConverters.get(name); + if (converter != null) + event = converter.toEvent(name, scriptArgs); + } + if (ctrl == null) + ctrl = new EventController(); + ctrl.scriptArgs = scriptArgs; + invokeImpl(rec, event, ctrl, scriptArgs); + } +} diff --git a/src/main/java/org/innercore/icstd/event/IEventListener.java b/src/main/java/org/innercore/icstd/event/IEventListener.java new file mode 100644 index 000000000..7d1373675 --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/IEventListener.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.event; + +public interface IEventListener { + void onEvent(T event); +} diff --git a/src/main/java/org/innercore/icstd/event/IManagedEventListener.java b/src/main/java/org/innercore/icstd/event/IManagedEventListener.java new file mode 100644 index 000000000..54a03ab74 --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/IManagedEventListener.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.event; + +public interface IManagedEventListener { + void onEvent(EventController ctrl, T event); +} diff --git a/src/main/java/org/innercore/icstd/event/IScriptEvent.java b/src/main/java/org/innercore/icstd/event/IScriptEvent.java new file mode 100644 index 000000000..e91b415a0 --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/IScriptEvent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.event; + +public interface IScriptEvent { + Object[] asScriptArgs(String name); +} diff --git a/src/main/java/org/innercore/icstd/event/IScriptEventConverter.java b/src/main/java/org/innercore/icstd/event/IScriptEventConverter.java new file mode 100644 index 000000000..5a55b911a --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/IScriptEventConverter.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.event; + +public interface IScriptEventConverter { + T toEvent(String name, Object[] args); +} diff --git a/src/main/java/org/innercore/icstd/event/IScriptEventListener.java b/src/main/java/org/innercore/icstd/event/IScriptEventListener.java new file mode 100644 index 000000000..63d932b51 --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/IScriptEventListener.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.event; + +public interface IScriptEventListener { + void onEvent(EventController ctrl, Object[] args); +} diff --git a/src/main/java/org/innercore/icstd/event/ScriptEventName.java b/src/main/java/org/innercore/icstd/event/ScriptEventName.java new file mode 100644 index 000000000..78b442c70 --- /dev/null +++ b/src/main/java/org/innercore/icstd/event/ScriptEventName.java @@ -0,0 +1,9 @@ +package org.innercore.icstd.event; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface ScriptEventName { + String value(); +} diff --git a/src/main/java/org/innercore/icstd/inventory/ItemContainer.java b/src/main/java/org/innercore/icstd/inventory/ItemContainer.java new file mode 100644 index 000000000..da2438923 --- /dev/null +++ b/src/main/java/org/innercore/icstd/inventory/ItemContainer.java @@ -0,0 +1,1087 @@ +package org.innercore.icstd.inventory; + +import org.jetbrains.annotations.Nullable; +import com.zhekasmirnov.apparatus.api.container.ItemContainerUiHandler; +import com.zhekasmirnov.apparatus.multiplayer.util.entity.SyncedNetworkData; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.ItemContainerBindGenBase; +import org.innercore.icstd.region.BlockSource; +import com.zhekasmirnov.apparatus.mcpe.NativePlayer; +import com.zhekasmirnov.apparatus.multiplayer.ThreadTypeMarker; +import com.zhekasmirnov.apparatus.multiplayer.server.ConnectedClient; +import com.zhekasmirnov.apparatus.multiplayer.util.entity.NetworkEntity; +import com.zhekasmirnov.apparatus.multiplayer.util.entity.NetworkEntityType; +import com.zhekasmirnov.apparatus.multiplayer.util.list.ConnectedClientList; +import com.zhekasmirnov.apparatus.util.Java8BackComp; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import com.zhekasmirnov.innercore.api.mod.recipes.workbench.WorkbenchField; +import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; +import com.zhekasmirnov.innercore.api.mod.ui.container.Container; +import com.zhekasmirnov.innercore.api.mod.ui.window.IWindow; +import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaver; +import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; + +import org.innercore.icstd.modules.ItemModule; +import org.innercore.icstd.serialization.*; +import org.json.JSONException; +import org.json.JSONObject; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +import java.util.*; + +@ApiBinding +public class ItemContainer extends ItemContainerBindGenBase implements WorkbenchField, ISerializableObject { + private static final Object playerInventoryLock = new Object(); + + public static void loadClass() { + // forces class to load and register listeners + } + + private static final Map screenFactoryMap = new HashMap<>(); + private static final Map> clientEventListenerMap = new HashMap<>(); + private static final Map> clientOnOpenListenerMap = new HashMap<>(); + private static final Map> clientOnCloseListenerMap = new HashMap<>(); + + @ApiBinding public static void registerScreenFactory(String name, UiScreenFactory factory) { + screenFactoryMap.put(name, factory); + } + + @ApiBinding public static void addClientEventListener(String typeName, String packetName, ClientEventListener listener) { + Java8BackComp.computeIfAbsent(clientEventListenerMap, typeName, key -> new HashMap<>()).put(packetName, listener); + } + + @ApiBinding public static void addClientOpenListener(String typeName, ClientOnOpenListener listener) { + Java8BackComp.computeIfAbsent(clientOnOpenListenerMap, typeName, key -> new ArrayList<>()).add(listener); + } + + @ApiBinding public static void addClientCloseListener(String typeName, ClientOnCloseListener listener) { + Java8BackComp.computeIfAbsent(clientOnCloseListenerMap, typeName, key -> new ArrayList<>()).add(listener); + } + + @ApiBinding public static ItemContainer getClientContainerInstance(String name) { + NetworkEntity entity = NetworkEntity.getClientEntityInstance(name); + if (entity != null) { + Object target = entity.getTarget(); + if (target instanceof ItemContainer) { + return (ItemContainer) target; + } + } + return null; + } + + + private static final String legacySaverId = ObjectSaverRegistry.registerSaverStr("_container2", new ObjectSaver() { + @Override + public Object read(ScriptableObject input) { + Container con = new Container(); + con.slots = input; + return new ItemContainer(con); + } + + @Override + public ScriptableObject save(Object input) { + if (input instanceof ItemContainer) { + return ((ItemContainer) input).asLegacyContainer(false).slots; + } + return null; + } + }); + private static final String saverId = ObjectSerializer.registerType("ic:item_container", ItemContainer.class, new IObjectSerializer() { + @Override + public ItemContainer read(IScopedReader reader, SerializationContext ctx, String typeId) { + ItemContainer con = new ItemContainer(); + IObjectReader slots = reader.asObject(); + if (slots == null) { + ctx.addError("container serializer expected a slot map"); + reader.endScope(); + return null; + } + for (String name : slots.iterKeys()) { + IArrayReader slot = slots.beginScope(name).asArray(); + if (slot != null && slot.size() > 2) { + ItemContainerSlot s = con.getSlot(name); + s.id = slot.getInt(0, 0); + s.count = slot.getInt(1, 0); + s.data = slot.getInt(2, 0); + if (slot.size() > 3) { + s.extra = ObjectSerializer.deserializeObject(slot.beginScope(3), ItemInstanceExtra.class, ctx, true); + } + } else if (slot != null && slot.size() == 0) { + con.getSlot(name); + } else { + ctx.addError("invalid container slot format " + name); + } + slots.endChildScope(); + } + reader.endScope(); + return con; + } + + @Override + public void write(IScopedWriter writer, ItemContainer container, SerializationContext ctx, String typeId) { + if (container == null) throw new IllegalArgumentException("container is null"); + IObjectWriter slots = writer.asObject(); + for (Map.Entry entry : container.slotMap.entrySet()) { + ItemContainerSlot s = entry.getValue(); + IArrayWriter slot = slots.beginScope(entry.getKey()).asArray(); + if (s.id == 0 && s.count == 0 && s.data == 0) { + slot.endScope(); // empty slot + continue; + } + slot.addInt(s.id); + slot.addInt(s.count); + slot.addInt(s.data); + if (s.extra != null && !s.extra.isEmpty()) { + ObjectSerializer.serializeObject(slot.beginScope(), s.extra, ctx, true); + } + slot.endScope(); + } + writer.endScope(); + } + }); + + @Override + public String getSerializationTypeId() { + return saverId; + } + + private static final NetworkEntityType containerNetworkEntityType = new NetworkEntityType("sys.container") + .setClientListSetupListener((list, target, entity) -> { + ItemContainer container = (ItemContainer) target; + list.addListener(new ConnectedClientList.Listener() { + @Override + public void onAdd(ConnectedClient client) { + // this event is sent too early, ui is not yet initialized, so on open listener is not called + } + + @Override + public void onRemove(ConnectedClient client) { + for (ServerOnCloseListener listener : container.onCloseListeners) { + listener.onClose(container, client); + } + } + }); + }) + .addServerPacketListener("close", (target, entity, client, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + container.closeFor(client); + }) + .addServerPacketListener("inv_to_slot", (target, entity, client, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + JSONObject packet = (JSONObject) packetData; + synchronized (container.transactionLock) { + long player = client.getPlayerUid(); + container.handleInventoryToSlotTransaction(player, packet.optInt("inv"), packet.optString("name"), packet.optInt("amount")); + container.sendChanges(); + } + }) + .addServerPacketListener("slot_to_inv", (target, entity, client, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + JSONObject packet = (JSONObject) packetData; + synchronized (container.transactionLock) { + long player = client.getPlayerUid(); + container.handleSlotToInventoryTransaction(player, packet.optString("slot"), packet.optInt("amount")); + container.sendChanges(); + } + }) + .addServerPacketListener("slot_to_slot", (target, entity, client, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + JSONObject packet = (JSONObject) packetData; + synchronized (container.transactionLock) { + long player = client.getPlayerUid(); + container.handleSlotToSlotTransaction(player, packet.optString("slot1"), packet.optString("slot2"), packet.optInt("amount")); + container.sendChanges(); + } + }) + .addServerPacketListener("bindings", (target, entity, client, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + JSONObject packet = (JSONObject) packetData; + container.handleDirtyBindingsPacket(client, packet); + container.sendChanges(); + }) + .addServerPacketListener("event", (target, entity, client, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + ServerEventListener listener = container.serverEventListenerMap.get(extra); + if (listener != null) { + listener.receive(container, client, packetData); + } + }) + .setClientAddPacketFactory((target, entity, client) -> { + ItemContainer container = (ItemContainer) target; + JSONObject packet = new JSONObject(); + + try { + JSONObject slots = new JSONObject(); + packet.put("clientType", container.clientContainerTypeName); + packet.put("slots", slots); + for (Map.Entry entry : container.slotMap.entrySet()) { + slots.put(entry.getKey(), entry.getValue().asJson()); + } + packet.put("bindings", new JSONObject(container.bindingsMap)); + } catch (JSONException ignore) { } + + return packet; + }) + .setClientEntityAddedListener((entity, initPacket) -> { + ItemContainer clientContainer = new ItemContainer(entity); + JSONObject json = (JSONObject) initPacket; + + JSONObject slots = json.optJSONObject("slots"); + if (slots != null) { + for (Iterator it = slots.keys(); it.hasNext(); ) { + String key = it.next(); + JSONObject slotJson = slots.optJSONObject(key); + if (slotJson != null) { + clientContainer.setSlot(key, new ItemContainerSlot(slotJson, true)); + } + } + } + + JSONObject bindings = json.optJSONObject("bindings"); + if (bindings != null) { + clientContainer.uiAdapter.receiveBindingsFromServer(bindings); + } + + clientContainer.setClientContainerTypeName(json.optString("clientType")); + return clientContainer; + }) + .setClientEntityRemovedListener((target, entity, removePacket) -> { + ItemContainer container = (ItemContainer) target; + container.closeUi(); + }) + .addClientPacketListener("open", (target, entity, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + container.openUi(packetData.toString()); + }) + .addClientPacketListener("slots", (target, entity, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + JSONObject slotsJson = (JSONObject) packetData; + if (slotsJson != null) { + synchronized (container.transactionLock) { + for (Iterator it = slotsJson.keys(); it.hasNext(); ) { + String key = it.next(); + JSONObject slotJson = slotsJson.optJSONObject(key); + if (slotJson != null) { + container.setSlot(key, new ItemContainerSlot(slotJson, true)); + } + } + } + } + }) + .addClientPacketListener("bindings", (target, entity, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + JSONObject bindingsJson = (JSONObject) packetData; + if (bindingsJson != null) { + container.uiAdapter.receiveBindingsFromServer(bindingsJson); + } + }) + .addClientPacketListener("event", (target, entity, packetData, extra) -> { + ItemContainer container = (ItemContainer) target; + Map listenerMap = clientEventListenerMap.get(container.clientContainerTypeName); + if (listenerMap != null) { + ClientEventListener listener = listenerMap.get(extra); + if (listener != null) { + IWindow window = container.uiAdapter.getWindow(); + listener.receive(container, window, window != null ? window.getContent() : null, packetData); + } + } + }); + + + public interface Transaction { + void run(ItemContainer container); + } + + public interface UiScreenFactory { + IWindow getByName(ItemContainer container, String name); + } + + public interface DirtySlotListener { + void onMarkedDirty(ItemContainer container, String name, ItemContainerSlot slot); + } + + public interface TransferPolicy { + int transfer(ItemContainer container, String name, int id, int count, int data, @Nullable ItemInstanceExtra extra, long player); + } + + public interface BindingValidator { + Object validate(ItemContainer container, String bindingComposedName, Object value, long player); + } + + public interface ClientEventListener { + void receive(ItemContainer container, IWindow window, ScriptableObject windowContent, Object packetData); + } + + public interface ServerEventListener { + void receive(ItemContainer container, ConnectedClient client, Object packetData); + } + + public interface ClientOnOpenListener { + void onOpen(ItemContainer container, String screenName); + } + + public interface ClientOnCloseListener { + void onClose(ItemContainer container); + } + + public interface ServerOnOpenListener { + void onOpen(ItemContainer container, ConnectedClient client, String screenName); + } + + public interface ServerOnCloseListener { + void onClose(ItemContainer container, ConnectedClient client); + } + + + @ApiBinding public final boolean isServer; + private final NetworkEntity networkEntity; + private final ItemContainerUiHandler uiAdapter = new ItemContainerUiHandler(this); + private Object parent = null; + + public final Object transactionLock = new Object(); + private final Map slotMap = new HashMap<>(); + private final Set dirtySlotSet = new HashSet<>(); + + private final Map bindingsMap = new HashMap<>(); + private final Map dirtyBindingsMap = new HashMap<>(); + + private TransferPolicy globalAddTransferPolicy = null; + private TransferPolicy globalGetTransferPolicy = null; + private final Map addTransferPolicyMap = new HashMap<>(); + private final Map getTransferPolicyMap = new HashMap<>(); + private DirtySlotListener globalDirtySlotListener = null; + private final Map dirtySlotListenerMap = new HashMap<>(); + + private BindingValidator globalBindingValidator = null; + private final Map bindingValidatorMap = new HashMap<>(); + + private String clientContainerTypeName = null; + private final Map serverEventListenerMap = new HashMap<>(); + private final List onOpenListeners = new ArrayList<>(); + private final List onCloseListeners = new ArrayList<>(); + + // container slots interface for legacy container interface + @ApiBinding public final Scriptable slots = ScriptableObjectHelper.createEmpty(); + + @ApiBinding public final SyncedNetworkData networkData; + + private ItemContainer(String entityName) { + ThreadTypeMarker.assertServerThread(); + this.isServer = true; + if (entityName != null) { + networkEntity = new NetworkEntity(containerNetworkEntityType, this, entityName); + } else { + networkEntity = new NetworkEntity(containerNetworkEntityType, this); + } + networkData = new SyncedNetworkData(networkEntity.getName()); + networkData.setClients(networkEntity.getClients()); + } + + private ItemContainer(NetworkEntity clientEntity) { + networkEntity = clientEntity; + isServer = false; + networkData = SyncedNetworkData.getClientSyncedData(clientEntity.getName()); + } + + @ApiBinding public ItemContainer() { + this((String) null); + } + + // copy legacy container + @ApiBinding public ItemContainer(Container container) { + this(); + for (Object id : container.slots.getAllIds()) { + Object slotScriptable = container.slots.get(id.toString(), container.slots); + if (slotScriptable instanceof ScriptableObject) { + setSlot(id.toString(), new ItemContainerSlot((ScriptableObject) slotScriptable)); + } + } + } + + @ApiBinding public NetworkEntity getNetworkEntity() { + return networkEntity; + } + + @ApiBinding public String getNetworkName() { + return networkEntity != null ? networkEntity.getName() : null; + } + + @ApiBinding public ItemContainerUiHandler getUiAdapter() { + if (isServer) { + throw new IllegalStateException(); + } + return uiAdapter; + } + + @ApiBinding public IWindow getWindow() { + return getUiAdapter().getWindow(); + } + + @ApiBinding("getWindowContent") public ScriptableObject getScriptWindowContent() { + IWindow window = getUiAdapter().getWindow(); + return window != null ? window.getContent() : null; + } + + @ApiBinding public void removeEntity() { + if (!isServer) { + throw new IllegalStateException(); + } + ThreadTypeMarker.assertServerThread(); + networkEntity.remove(); + } + + @ApiBinding @Deprecated public void setParent(Object parent) { + this.parent = parent; + } + + @ApiBinding @Deprecated public Object getParent() { + return parent; + } + + + @ApiBinding public ItemContainer setGlobalAddTransferPolicy(TransferPolicy globalAddTransferPolicy) { + this.globalAddTransferPolicy = globalAddTransferPolicy; + return this; + } + + @ApiBinding public ItemContainer setGlobalGetTransferPolicy(TransferPolicy globalGetTransferPolicy) { + this.globalGetTransferPolicy = globalGetTransferPolicy; + return this; + } + + @ApiBinding public ItemContainer setSlotAddTransferPolicy(String name, TransferPolicy policy) { + addTransferPolicyMap.put(name, policy); + return this; + } + + @ApiBinding public ItemContainer setSlotGetTransferPolicy(String name, TransferPolicy policy) { + getTransferPolicyMap.put(name, policy); + return this; + } + + @ApiBinding public ItemContainer setGlobalDirtySlotListener(DirtySlotListener globalDirtySlotListener) { + this.globalDirtySlotListener = globalDirtySlotListener; + return this; + } + + @ApiBinding public ItemContainer setDirtySlotListener(String name, DirtySlotListener listener) { + dirtySlotListenerMap.put(name, listener); + return this; + } + + @ApiBinding public void sealSlot(String slotName) { + setSlotAddTransferPolicy(slotName, (container, name, id, count, data, extra, player) -> 0); + setSlotGetTransferPolicy(slotName, (container, name, id, count, data, extra, player) -> 0); + } + + @ApiBinding public void sealAllSlots() { + setGlobalAddTransferPolicy((container, name, id, count, data, extra, player) -> 0); + setGlobalGetTransferPolicy((container, name, id, count, data, extra, player) -> 0); + } + + @ApiBinding public TransferPolicy getAddTransferPolicy(String slot) { + return Java8BackComp.getOrDefault(addTransferPolicyMap, slot, globalAddTransferPolicy); + } + + @ApiBinding public TransferPolicy getGetTransferPolicy(String slot) { + return Java8BackComp.getOrDefault(getTransferPolicyMap, slot, globalGetTransferPolicy); + } + + + @ApiBinding public void setGlobalBindingValidator(BindingValidator globalBindingValidator) { + this.globalBindingValidator = globalBindingValidator; + } + + @ApiBinding public void setBindingValidator(String composedBindingName, BindingValidator validator) { + this.bindingValidatorMap.put(composedBindingName, validator); + } + + @ApiBinding public BindingValidator getBindingValidator(String composedBindingName) { + return Java8BackComp.getOrDefault(bindingValidatorMap, composedBindingName, globalBindingValidator); + } + + + + @ApiBinding public void runTransaction(Transaction transaction) { + synchronized (transactionLock) { + transaction.run(this); + } + } + + @ApiBinding public ItemContainerSlot getSlot(String name) { + if (!slotMap.containsKey(name)) { + ItemContainerSlot slot = new ItemContainerSlot(); + slot.setContainer(this, name); + slotMap.put(name, slot); + slots.put(name, slots, slot); + return slot; + } else { + return slotMap.get(name); + } + } + + @ApiBinding @Deprecated public ItemContainerSlot getFullSlot(String name) { + return getSlot(name); + } + + + @ApiBinding public void markSlotDirty(String name) { + synchronized (transactionLock) { + if (isServer) { + ItemContainerSlot slot = slotMap.get(name); + if (slot != null) { + dirtySlotSet.add(name); + if (globalDirtySlotListener != null) { + globalDirtySlotListener.onMarkedDirty(this, name, slot); + } + DirtySlotListener listener = dirtySlotListenerMap.get(name); + if (listener != null) { + listener.onMarkedDirty(this, name, slot); + } + } + } + } + } + + @ApiBinding public void markAllSlotsDirty() { + synchronized (transactionLock) { + if (isServer) { + for (String name : slotMap.keySet()) { + markSlotDirty(name); + } + } + } + } + + @ApiBinding public void setSlot(String name, ItemContainerSlot slot) { + slot.setContainer(this, name); + slotMap.put(name, slot); + slots.put(name, slots, slot); + markSlotDirty(name); + } + + @ApiBinding public void setSlot(String name, int id, int count, int data, @Nullable ItemInstanceExtra extra) { + ItemContainerSlot slot = slotMap.get(name); + if (slot != null) { + slot.setSlot(id, count, data, extra); + } else { + setSlot(name, new ItemContainerSlot(id, count, data, extra)); + } + } + + @ApiBinding public void setSlot(String name, int id, int count, int data) { + setSlot(name, id, count, data, null); + } + + @ApiBinding public int addToSlot(String name, int id, int count, int data, @Nullable ItemInstanceExtra extra, long player) { + if (count <= 0) { + return 0; + } + ItemContainerSlot slot = getSlot(name); + if (slot != null && (slot.id == 0 || slot.id == id && slot.data == data && slot.extra == null)) { + int amount; + TransferPolicy policy = getAddTransferPolicy(name); + if (policy != null) { + amount = policy.transfer(this, name, id, count, data, extra, player); + } else { + amount = Math.min(slot.count + count, ItemModule.getMaxStack(id, data)) - slot.count; + } + if (amount > 0) { + slot.count += amount; + slot.id = id; + slot.data = data; + slot.extra = extra; + markSlotDirty(name); + return amount; + } + } + return 0; + } + + @ApiBinding public int getFromSlot(String name, int id, int count, int data, @Nullable ItemInstanceExtra extra, long player) { + if (count <= 0) { + return 0; + } + ItemContainerSlot slot = getSlot(name); + if (slot != null && slot.id == id && slot.data == data && (slot.extra == null || slot.extra.equals(extra))) { + TransferPolicy policy = getGetTransferPolicy(name); + int amount = Math.min(slot.count, count); + if (policy != null) { + amount = policy.transfer(this, name, id, amount, data, extra, player); + } + if (amount > 0) { + slot.count -= amount; + slot.validate(); + markSlotDirty(name); + return amount; + } + } + return 0; + } + + + @ApiBinding public void sendChanges() { + if (isServer) { + if (networkEntity.getClients().getClientCollectionsNoRefresh().isEmpty()) { + return; + } + if (!dirtySlotSet.isEmpty()) { + JSONObject slotPacket = new JSONObject(); + synchronized (transactionLock) { + try { + for (String name : dirtySlotSet) { + ItemContainerSlot slot = slotMap.get(name); + if (slot != null) { + slotPacket.put(name, slot.asJson()); + } + } + dirtySlotSet.clear(); + } catch (JSONException ignore) { + } + } + networkEntity.send("slots", slotPacket); + } + if (!dirtyBindingsMap.isEmpty()) { + synchronized (bindingsMap) { + networkEntity.send("bindings", new JSONObject(dirtyBindingsMap)); + dirtyBindingsMap.clear(); + } + } + } + } + + @ApiBinding public void dropAt(BlockSource blockSource, float x, float y, float z) { + synchronized (transactionLock) { + for (ItemContainerSlot slot : slotMap.values()) { + slot.dropAt(blockSource, x, y, z); + } + } + sendChanges(); + } + + @ApiBinding public void validateAll() { + synchronized (transactionLock) { + for (ItemContainerSlot slot : slotMap.values()) { + slot.validate(); + } + } + sendChanges(); + } + + @ApiBinding public void validateSlot(String name) { + getSlot(name).validate(); + } + + @ApiBinding public void clearSlot(String name) { + getSlot(name).clear(); + } + + @ApiBinding public void dropSlot(BlockSource blockSource, String name, float x, float y, float z) { + getSlot(name).dropAt(blockSource, x, y, z); + } + + + // default transactions + + @ApiBinding public void sendInventoryToSlotTransaction(int inventorySlot, String slotName, int amount) { + if (isServer) { + throw new IllegalStateException(); + } + try { + JSONObject packet = new JSONObject(); + packet.put("inv", inventorySlot); + packet.put("name", slotName); + packet.put("amount", amount); + networkEntity.send("inv_to_slot", packet); + } catch (JSONException ignore) { } + } + + void handleInventoryToSlotTransaction(long playerUid, int inventorySlot, String slotName, int amount) { + if (!isServer) { + throw new IllegalStateException(); + } + + if (slotName != null && amount > 0 && inventorySlot >= 0 && inventorySlot < 36) { + NativePlayer player = new NativePlayer(playerUid); + if (player.isValid()) { + synchronized (playerInventoryLock) { + ItemInstance itemInstance = player.getInventorySlot(inventorySlot); + if (itemInstance.id != 0 && itemInstance.count > 0) { + int id = itemInstance.id; + int count = itemInstance.count; + int data = itemInstance.data; + ItemInstanceExtra extra = itemInstance.extra; + if (amount > itemInstance.count) { + amount = itemInstance.count; + } + amount = addToSlot(slotName, id, amount, data, extra, playerUid); + count -= amount; + if (count > 0) { + player.setInventorySlot(inventorySlot, id, count, data, extra); + } else { + player.setInventorySlot(inventorySlot, 0, 0, 0, null); + } + } + } + } + } + } + + @ApiBinding public void sendSlotToSlotTransaction(String slot1, String slot2, int amount) { + if (isServer) { + throw new IllegalStateException(); + } + try { + JSONObject packet = new JSONObject(); + packet.put("slot1", slot1); + packet.put("slot2", slot2); + packet.put("amount", amount); + networkEntity.send("slot_to_slot", packet); + } catch (JSONException ignore) { } + } + + void handleSlotToSlotTransaction(long player, String slotName1, String slotName2, int amount) { + if (!isServer) { + throw new IllegalStateException(); + } + } + + @ApiBinding public void sendSlotToInventoryTransaction(String slot, int amount) { + if (isServer) { + throw new IllegalStateException(); + } + try { + JSONObject packet = new JSONObject(); + packet.put("slot", slot); + packet.put("amount", amount); + networkEntity.send("slot_to_inv", packet); + } catch (JSONException ignore) { } + } + + void handleSlotToInventoryTransaction(long playerUid, String slotName, int amount) { + if (!isServer) { + throw new IllegalStateException(); + } + + if (amount > 0) { + ItemContainerSlot slot = getSlot(slotName); + if (slot.id != 0 && slot.count > 0) { + NativePlayer player = new NativePlayer(playerUid); + if (player.isValid()) { + int id = slot.id; + int data = slot.data; + ItemInstanceExtra extra = slot.extra; + amount = getFromSlot(slotName, id, amount, data, extra, playerUid); + if (amount > 0) { + synchronized (playerInventoryLock) { + player.addItemToInventory(id, amount, data, extra, true); + } + } + } + } + } + } + + + void sendDirtyClientBinding(String key, Object value) { + if (!isServer) { + throw new IllegalStateException(); + } + try { + JSONObject packet = new JSONObject(); + packet.put(key, value); + networkEntity.send("bindings", packet); + } catch (JSONException ignore) { } + } + + void handleDirtyBindingsPacket(ConnectedClient client, JSONObject packet) { + long playerUid = client.getPlayerUid(); + for (Iterator it = packet.keys(); it.hasNext(); ) { + String key = it.next(); + Object value = packet.opt(key); + if (value instanceof Boolean || value instanceof Number || value instanceof String) { + BindingValidator validator = getBindingValidator(key); + if (validator != null) { + value = validator.validate(this, key, value, playerUid); + } + if (value != null) { + synchronized (bindingsMap) { + bindingsMap.put(key, value); + dirtyBindingsMap.put(key, value); + } + } + } + } + } + + + @ApiBinding public void setBinding(String composedBindingName, Object value) { + if (value instanceof CharSequence) { + value = value.toString(); + if (value == null) value = ""; // just in case because apparently this can happen + } else if (value instanceof Number) { + if (value instanceof Float || value instanceof Double) { + if (!Float.isFinite(((Number) value).floatValue())) + value = 0.f; + } + } else if (!(value instanceof Boolean)) { + throw new IllegalArgumentException("binding value must be number, string or boolean"); + } + synchronized (bindingsMap) { + bindingsMap.put(composedBindingName, value); + dirtyBindingsMap.put(composedBindingName, value); + } + if (!isServer) { + // also update this binding on client + getUiAdapter().setBindingByComposedName(composedBindingName, value); + } + } + + @ApiBinding public void setClientBinding(String composedBindingName, Object value) { + getUiAdapter().setBindingByComposedName(composedBindingName, value); + synchronized (bindingsMap) { + bindingsMap.put(composedBindingName, value); + } + } + + @ApiBinding public Object getBinding(String composedBindingName) { + return bindingsMap.get(composedBindingName); + } + + @ApiBinding public void setBinding(String elementName, String bindingName, Object value) { + setBinding(elementName + "::" + bindingName, value); + } + + @ApiBinding public void setClientBinding(String elementName, String bindingName, Object value) { + setClientBinding(elementName + "::" + bindingName, value); + } + + @ApiBinding public Object getBinding(String elementName, String bindingName) { + return getBinding(elementName + "::" + bindingName); + } + + @ApiBinding public void setScale(String elementName, float value) { + setBinding(elementName, "value", value); + } + + @ApiBinding public void setClientScale(String elementName, float value) { + setClientBinding(elementName, "value", value); + } + + @ApiBinding public Object getValue(String elementName, float value) { + return getBinding(elementName, "value"); + } + + @ApiBinding public void setText(String elementName, String text) { + setBinding(elementName, "text", text); + } + + @ApiBinding public void setClientText(String elementName, String text) { + setClientBinding(elementName, "text", text); + } + + @ApiBinding public String getText(String elementName) { + Object binding = getBinding(elementName, "text"); + return binding instanceof CharSequence ? binding.toString() : null; + } + + + @ApiBinding public void setClientContainerTypeName(String clientContainerTypeName) { + this.clientContainerTypeName = clientContainerTypeName; + } + + @ApiBinding public String getClientContainerTypeName() { + return clientContainerTypeName; + } + + @ApiBinding public void addServerEventListener(String name, ServerEventListener listener) { + serverEventListenerMap.put(name, listener); + } + + @ApiBinding public void addServerOpenListener(ServerOnOpenListener listener) { + onOpenListeners.add(listener); + } + + @ApiBinding public void addServerCloseListener(ServerOnCloseListener listener) { + onCloseListeners.add(listener); + } + + @ApiBinding public void sendEvent(String name, Object data) { + if (!isServer) { + networkEntity.getClientExecutor().add(() -> networkEntity.send("event#" + name, data)); + } else { + networkEntity.send("event#" + name, data); + } + } + + @ApiBinding public void sendEvent(ConnectedClient client, String name, Object data) { + if (!isServer) { + networkEntity.getClientExecutor().add(() -> networkEntity.send(client, "event#" + name, data)); + } else { + networkEntity.send(client, "event#" + name, data); + } + } + + @ApiBinding public void sendResponseEvent(String name, Object data) { + if (!isServer) { + networkEntity.getClientExecutor().add(() -> networkEntity.respond("event#" + name, data)); + } else { + networkEntity.respond("event#" + name, data); + } + } + + + @ApiBinding public void openFor(ConnectedClient client, String screenName) { + if (!isServer) { + throw new IllegalStateException(); + } + networkEntity.getClients().add(client); + networkEntity.send(client, "open", screenName); + for (ServerOnOpenListener listener : onOpenListeners) { + listener.onOpen(this, client, screenName); + } + } + + @ApiBinding public void closeFor(ConnectedClient client) { + if (!isServer) { + throw new IllegalStateException(); + } + networkEntity.getClients().remove(client); + } + + @ApiBinding public void close() { + if (!isServer) { + throw new IllegalStateException(); + } + networkEntity.getClients().clear(); + } + + + @ApiBinding public void sendClosed() { + if (isServer) { + throw new IllegalStateException(); + } + List onCloseListener = clientOnCloseListenerMap.get(clientContainerTypeName); + if (onCloseListener != null) { + for (ClientOnCloseListener listener : onCloseListener) { + listener.onClose(this); + } + } + networkEntity.send("close", ""); + } + + private void closeUi() { + if (isServer) { + throw new IllegalStateException(); + } + uiAdapter.close(); + } + + private void openUi(String name) { + if (isServer) { + throw new IllegalStateException(); + } + if (clientContainerTypeName != null) { + List onOpenListeners = clientOnOpenListenerMap.get(clientContainerTypeName); + if (onOpenListeners != null) { + for (ClientOnOpenListener listener : onOpenListeners) { + listener.onOpen(this, name); + } + } + + UiScreenFactory factory = screenFactoryMap.get(clientContainerTypeName); + if (factory != null) { + IWindow window = factory.getByName(this, name); + if (window != null) { + uiAdapter.openAs(window); + return; + } + } + } + sendClosed(); + } + + + // saves + + private boolean globalSlotSavingEnabled = true; + + @ApiBinding public void setGlobalSlotSavingEnabled(boolean globalSlotSavingEnabled) { + this.globalSlotSavingEnabled = globalSlotSavingEnabled; + } + + @ApiBinding public boolean isGlobalSlotSavingEnabled() { + return globalSlotSavingEnabled; + } + + @ApiBinding public void setSlotSavingEnabled(String name, boolean enabled) { + getSlot(name).setSavingEnabled(enabled); + } + + @ApiBinding public void resetSlotSavingEnabled(String name) { + getSlot(name).resetSavingEnabled(); + } + + @ApiBinding public boolean isSlotSavingEnabled(String name) { + return slotMap.containsKey(name) ? getSlot(name).isSavingEnabled() : globalSlotSavingEnabled; + } + + + // legacy container backcomp + + @ApiBinding @Deprecated public boolean isLegacyContainer() { + return false; + } + + @ApiBinding public Container asLegacyContainer(boolean allSlots) { + Container container = new Container(this); + for (Map.Entry entry : slotMap.entrySet()) { + ItemContainerSlot slot = entry.getValue(); + if (!slot.isEmpty() && (allSlots || slot.isSavingEnabled())) { + container.slots.put(entry.getKey(), container.slots, slot.asScriptable()); + } + } + return container; + } + + @ApiBinding public Container asLegacyContainer() { + return asLegacyContainer(true); + } + + + private String legacyWorkbenchFieldPrefix = null; + private int workbenchFieldSize = 3; + + @ApiBinding public void setWorkbenchFieldPrefix(String legacyWorkbenchFieldPrefix) { + this.legacyWorkbenchFieldPrefix = legacyWorkbenchFieldPrefix; + } + + @ApiBinding public void setWorkbenchFieldSize(int workbenchFieldSize) { + this.workbenchFieldSize = workbenchFieldSize; + } + + @ApiBinding @Override public AbstractSlot getFieldSlot(int index) { + return getSlot(legacyWorkbenchFieldPrefix + index); + } + + @ApiBinding @Override public AbstractSlot getFieldSlot(int x, int y) { + if (x >= 0 && y >= 0 && x < workbenchFieldSize && y < workbenchFieldSize) { + return getSlot(legacyWorkbenchFieldPrefix + (y * workbenchFieldSize + x)); + } else { + return null; + } + } + + @ApiBinding @Override public Scriptable asScriptableField() { + Object[] field = new Object[workbenchFieldSize * workbenchFieldSize]; + for (int i = 0; i < field.length; i++) { + field[i] = getFieldSlot(i); + } + return ScriptableObjectHelper.createArray(field); + } + + @ApiBinding @Override public int getWorkbenchFieldSize() { + return workbenchFieldSize; + } +} diff --git a/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainerSlot.java b/src/main/java/org/innercore/icstd/inventory/ItemContainerSlot.java similarity index 53% rename from src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainerSlot.java rename to src/main/java/org/innercore/icstd/inventory/ItemContainerSlot.java index 3ec3ff8b6..17a453100 100644 --- a/src/main/java/com/zhekasmirnov/apparatus/api/container/ItemContainerSlot.java +++ b/src/main/java/org/innercore/icstd/inventory/ItemContainerSlot.java @@ -1,63 +1,70 @@ -package com.zhekasmirnov.apparatus.api.container; - -import com.zhekasmirnov.apparatus.mcpe.NativeBlockSource; -import com.zhekasmirnov.innercore.api.NativeItemInstanceExtra; +package org.innercore.icstd.inventory; + +import org.jetbrains.annotations.Nullable; +import com.zhekasmirnov.innercore.api.gen.ItemContainerSlotBindGenBase; +import org.innercore.icstd.annotations.ApiBinding; +import org.innercore.icstd.annotations.ApiHidden; +import org.innercore.icstd.region.BlockSource; +import com.zhekasmirnov.apparatus.multiplayer.mod.IdConversionMap; import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; import com.zhekasmirnov.innercore.api.mod.ui.container.AbstractSlot; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.javascript.ScriptableObject; -public class ItemContainerSlot implements AbstractSlot { - public int id, count, data; - public NativeItemInstanceExtra extra; +@ApiBinding +public class ItemContainerSlot extends ItemContainerSlotBindGenBase implements AbstractSlot { + @ApiBinding public int id; + @ApiBinding public int count; + @ApiBinding public int data; + @ApiBinding @Nullable public ItemInstanceExtra extra; private String name; private ItemContainer container; private Boolean isSavingEnabled = null; - public ItemContainerSlot(int id, int count, int data, NativeItemInstanceExtra extra) { + @ApiHidden public ItemContainerSlot(int id, int count, int data, @Nullable ItemInstanceExtra extra) { this.id = id; this.count = count; this.data = data; this.extra = extra; } - public ItemContainerSlot(int id, int count, int data) { + @ApiHidden public ItemContainerSlot(int id, int count, int data) { this(id, count, data, null); } - public ItemContainerSlot() { + @ApiHidden public ItemContainerSlot() { this(0, 0, 0, null); } - public ItemContainerSlot(ScriptableObject scriptable) { + @ApiHidden public ItemContainerSlot(ScriptableObject scriptable) { this( ScriptableObjectHelper.getIntProperty(scriptable, "id", 0), ScriptableObjectHelper.getIntProperty(scriptable, "count", 0), ScriptableObjectHelper.getIntProperty(scriptable, "data", 0), - NativeItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(scriptable, "extra", null)) + ItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(scriptable, "extra", null)) ); } - public ItemContainerSlot(JSONObject json, boolean convert) { - this(convert ? json.optInt("id", 0) : json.optInt("id", 0), json.optInt("count", 0), json.optInt("data", 0), NativeItemInstanceExtra.fromJson(json.optJSONObject("extra"))); + @ApiHidden public ItemContainerSlot(JSONObject json, boolean convert) { + this(convert ? IdConversionMap.serverToLocal(json.optInt("id", 0)) : json.optInt("id", 0), json.optInt("count", 0), json.optInt("data", 0), ItemInstanceExtra.fromJson(json.optJSONObject("extra"))); } - void setContainer(ItemContainer container, String name) { + @ApiHidden void setContainer(ItemContainer container, String name) { this.name = name; this.container = container; } - public String getName() { + @ApiBinding public String getName() { return name; } - public ItemContainer getContainer() { + @ApiBinding public ItemContainer getContainer() { return container; } - public ScriptableObject asScriptable() { + @ApiHidden public ScriptableObject asScriptable() { ScriptableObject object = ScriptableObjectHelper.createEmpty(); object.put("id", object, id); object.put("count", object, count); @@ -66,7 +73,7 @@ public ScriptableObject asScriptable() { return object; } - public JSONObject asJson() { + @ApiHidden public JSONObject asJson() { JSONObject json = new JSONObject(); try { json.put("id", id); @@ -82,34 +89,34 @@ public JSONObject asJson() { return json; } - public boolean isEmpty() { + @ApiBinding public boolean isEmpty() { return this.id == 0 && this.count == 0 && this.data == 0 && this.extra == null; } - public void markDirty() { + @ApiBinding public void markDirty() { if (this.container != null) { this.container.markSlotDirty(name); } } - public void clear() { + @ApiBinding public void clear() { this.id = this.count = this.data = 0; this.extra = null; this.markDirty(); } - public void validate() { + @ApiBinding public void validate() { if (this.id == 0 || this.count <= 0) { this.clear(); } } - public void dropAt(NativeBlockSource blockSource, float x, float y, float z) { + @ApiBinding public void dropAt(BlockSource blockSource, float x, float y, float z) { blockSource.spawnDroppedItem(x, y, z, id, count, data, extra); this.clear(); } - public void setSlot(int id, int count, int data, NativeItemInstanceExtra extra) { + @ApiBinding public void setSlot(int id, int count, int data, @Nullable ItemInstanceExtra extra) { this.id = id; this.count = count; this.data = data; @@ -117,44 +124,43 @@ public void setSlot(int id, int count, int data, NativeItemInstanceExtra extra) this.markDirty(); } - public void setSlot(int id, int count, int data) { + @ApiBinding public void setSlot(int id, int count, int data) { this.setSlot(id, count, data, null); } - public void resetSavingEnabled() { + @ApiBinding public void resetSavingEnabled() { isSavingEnabled = null; } - public void setSavingEnabled(boolean savingEnabled) { + @ApiBinding public void setSavingEnabled(boolean savingEnabled) { isSavingEnabled = savingEnabled; } - public boolean isSavingEnabled() { + @ApiBinding public boolean isSavingEnabled() { return isSavingEnabled != null ? isSavingEnabled : (container != null && container.isGlobalSlotSavingEnabled()); } - public int getId() { + @ApiBinding @Override public int getId() { return id; } - public int getCount() { + @ApiBinding @Override public int getCount() { return count; } - public int getData() { + @ApiBinding @Override public int getData() { return data; } - public NativeItemInstanceExtra getExtra() { + @ApiBinding @Override @Nullable public ItemInstanceExtra getExtra() { return extra; } - public void set(int id, int count, int data, NativeItemInstanceExtra extra) { + @ApiBinding @Override public void set(int id, int count, int data, @Nullable ItemInstanceExtra extra) { setSlot(id, count, data, extra); } - @Override - public String toString() { + @Override public String toString() { return "ItemContainerSlot{" + "id=" + id + ", count=" + count + diff --git a/src/main/java/org/innercore/icstd/inventory/ItemInstance.java b/src/main/java/org/innercore/icstd/inventory/ItemInstance.java new file mode 100644 index 000000000..a07a63235 --- /dev/null +++ b/src/main/java/org/innercore/icstd/inventory/ItemInstance.java @@ -0,0 +1,172 @@ +package org.innercore.icstd.inventory; + +import cn.nukkit.item.Item; +import com.reider745.hooks.item.ItemUtils; +import com.reider745.item.NukkitIdConverter; +import com.zhekasmirnov.innercore.api.NativeItemInstance; +import com.zhekasmirnov.innercore.api.NativeItemModel; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.ItemInstanceBindGenBase; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import org.innercore.icstd.modules.ItemModule; +import org.jetbrains.annotations.Nullable; +import org.json.JSONException; +import org.json.JSONObject; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.Undefined; +import org.mozilla.javascript.Wrapper; + +@ApiBinding +public class ItemInstance extends ItemInstanceBindGenBase { + @ApiBinding public int id = 0; + @ApiBinding public int count = 0; + @ApiBinding public int data = 0; + @ApiBinding @Nullable public ItemInstanceExtra extra = null; + + @ApiBinding + public ItemInstance(int id, int count, int data, @Nullable ItemInstanceExtra extra) { + this.id = id; + this.count = count; + this.data = data; + this.extra = extra; + } + + @ApiBinding + public ItemInstance(int id, int count, int data) { + this(id, count, data, null); + } + + @ApiBinding + public ItemInstance(ItemInstance rhs) { + if (rhs == null) return; + this.id = rhs.id; + this.count = rhs.count; + this.data = rhs.data; + this.extra = rhs.extra; + } + + @ApiBinding + public ItemInstance() { this(0, 0, 0, null); } + + @ApiBinding + public void setItem(int id, int count, int data) { + this.id = id; + this.count = count; + this.data = data; + } + + @ApiBinding + public ItemInstance(Scriptable scriptable) { + this( + ScriptableObjectHelper.getIntProperty(scriptable, "id", 0), + ScriptableObjectHelper.getIntProperty(scriptable, "count", 0), + ScriptableObjectHelper.getIntProperty(scriptable, "data", 0), + ItemInstanceExtra.unwrapObject(ScriptableObjectHelper.getProperty(scriptable, "extra", null)) + ); + } + + public static ItemInstance makeFromPointer(long pointer, boolean nullable) { + if (pointer != 0) { + NativeItemInstance instance = new NativeItemInstance(pointer); + ItemInstance item = new ItemInstance(instance.id, instance.count, instance.data, instance.extra); + instance.destroy(); + return item; + } + return nullable ? null : new ItemInstance(); + } + + public static ItemInstance makeFromPointer(long pointer) { return makeFromPointer(pointer, false); } + + public static ItemInstance makeFrom(Item item) { + final ItemInstance instance = new ItemInstance(item.getId(), item.getCount(), item.getDamage(), ItemUtils.getItemInstanceExtra(item)); + NukkitIdConverter.convert(instance); + return instance; + } + + @ApiBinding + public static ItemInstance parse(Object obj, boolean nullable) { + while (obj instanceof Wrapper) { + obj = ((Wrapper) obj).unwrap(); + } + if (obj == null || obj == Undefined.instance) { + return nullable ? null : new ItemInstance(); + } else if (obj instanceof ItemInstance) { + return (ItemInstance) obj; + } else if (obj instanceof Scriptable) { + return new ItemInstance((Scriptable) obj); + } else if (obj instanceof JSONObject || obj instanceof CharSequence) { + JSONObject json; + try { + json = obj instanceof JSONObject ? (JSONObject) obj : new JSONObject(obj.toString()); + } catch (JSONException e) { + return nullable ? null : new ItemInstance(); + } + return new ItemInstance(json.optInt("id", 0), json.optInt("count", 0), json.optInt("data", 0), ItemInstanceExtra.fromJson(json.optJSONObject("extra"))); + } else if (obj instanceof Long) { + return ItemInstance.makeFromPointer((Long) obj, nullable); + } + return nullable ? null : new ItemInstance(); + } + + @ApiBinding + public JSONObject asJson() { + JSONObject json = new JSONObject(); + try { + json.put("id", id); + json.put("count", count); + json.put("data", data); + if (extra != null) { + JSONObject extraJson = extra.asJson(); + if (extraJson != null) { + json.put("extra", extraJson); + } + } + } catch (JSONException ignore) { } + return json; + } + + @ApiBinding + public boolean isEmpty() { + return id == 0 && count == 0 && data == 0 && extra == null; + } + + @ApiBinding + public long getExtraPtr() { + return ItemInstanceExtra.getValueOrNullPtr(extra); + } + + @ApiBinding + public int getMaxStackSize() { + return ItemModule.getMaxStack(id, data); + } + + @ApiBinding + public int getMaxDamage() { + return ItemModule.getMaxDamage(id, data); + } + + @ApiBinding + public String getItemName() { + return ItemModule.getName(id, data, getExtraPtr()); + } + + @ApiBinding + public boolean isGlint() { + return ItemModule.isGlint(id, data, extra); + } + + @ApiBinding + public NativeItemModel getItemModel() { + return NativeItemModel.getForWithFallback(id, data); + } + + @Override + public String toString() { + return "ItemInstance{" + + "id=" + id + + ", count=" + count + + ", data=" + data + + (extra != null ? ", extra=" + extra : "") + + '}'; + } +} diff --git a/src/main/java/org/innercore/icstd/inventory/ItemInstanceExtra.java b/src/main/java/org/innercore/icstd/inventory/ItemInstanceExtra.java new file mode 100644 index 000000000..d7a387529 --- /dev/null +++ b/src/main/java/org/innercore/icstd/inventory/ItemInstanceExtra.java @@ -0,0 +1,720 @@ +package org.innercore.icstd.inventory; + +import cn.nukkit.item.Item; +import cn.nukkit.item.enchantment.Enchantment; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.nbt.tag.ListTag; +import com.reider745.api.ZoteOnly; +import com.reider745.api.pointers.PointersStorage; +import com.reider745.api.pointers.pointer_gen.PointerGenSlowest; +import com.reider745.hooks.item.ItemUtils; +import com.zhekasmirnov.horizon.runtime.logger.Logger; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.ItemExtraDataBindGenBase; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import com.zhekasmirnov.innercore.api.nbt.NativeCompoundTag; +import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaver; +import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; +import com.zhekasmirnov.innercore.api.runtime.saver.serializer.ScriptableSerializer; +import org.innercore.icstd.serialization.*; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Wrapper; + +/** + * Created by zheka on 14.02.2018. + */ + +@ApiBinding("ItemExtraData") +public class ItemInstanceExtra extends ItemExtraDataBindGenBase implements ISerializableObject { + private static final PointersStorage ITEMS = new PointersStorage<>("items", new PointerGenSlowest()); + private static final String legacySaverId = ObjectSaverRegistry.registerSaverStr("_item_instance_extra_data", new ObjectSaver() { + @Override + public Object read(ScriptableObject input) { + if (input == null) { + return null; + } + + ItemInstanceExtra extra = new ItemInstanceExtra(); + + NativeArray _ids = ScriptableObjectHelper.getNativeArrayProperty(input, "eId", null); + NativeArray _levels = ScriptableObjectHelper.getNativeArrayProperty(input, "eLvl", null); + if (_ids != null && _levels != null) { + Object[] ids = _ids.toArray(); + Object[] levels = _levels.toArray(); + int count = Math.min(ids.length, levels.length); + for (int i = 0; i < count; i++) { + extra.addEnchant((int) ids[i], (int) levels[i]); + } + } + + String custom = ScriptableObjectHelper.getStringProperty(input, "$", null); + if (custom != null) { + extra.setAllCustomData(custom); + } + + String name = ScriptableObjectHelper.getStringProperty(input, "N", null); + if (name != null) { + extra.setCustomName(name); + } + + return extra.isEmpty() ? null : extra; + } + + @Override + public ScriptableObject save(Object input) { + if (input != null && input instanceof ItemInstanceExtra) { + ItemInstanceExtra extra = (ItemInstanceExtra) input; + if (!extra.isEmpty()) { + ScriptableObject saved = ScriptableObjectHelper.createEmpty(); + + if (extra.isEnchanted()) { + int[][] enchants = extra.getRawEnchants(); + Object[] ids = new Object[enchants[0].length]; + Object[] levels = new Object[enchants[1].length]; + for (int i = 0; i < ids.length; i++) { + ids[i] = enchants[0][i]; + levels[i] = enchants[1][i]; + } + saved.put("eId", saved, ScriptableObjectHelper.createArray(ids)); + saved.put("eLvl", saved, ScriptableObjectHelper.createArray(levels)); + } + + String custom = extra.getAllCustomData(); + if (custom != null) { + saved.put("$", saved, custom); + } + + String name = extra.getCustomName(); + if (name != null) { + saved.put("N", saved, name); + } + + return saved; + } + } + return null; + } + }); + + private static final String saverId = ObjectSerializer.registerType("ic:ii_ext", ItemInstanceExtra.class, new IObjectSerializer() { + @Override + public ItemInstanceExtra read(IScopedReader reader, SerializationContext ctx, String typeId) { + if (reader.isEmpty()) + return null; + if (!reader.isObject()) { + ctx.addError("ItemInstanceExtra invalid format"); + return null; + } + ItemInstanceExtra extra = new ItemInstanceExtra(); + IObjectReader o = reader.asObject(); + IArrayReader enchants = o.beginScope("enc").asArray(); + if (enchants != null) { + for (int i = 0; i < enchants.size() - 1; i += 2) { + extra.addEnchant(enchants.getInt(i, 0), enchants.getInt(i + 1, 0)); + } + } + o.endChildScope(); + String customName = o.getString("nm", null); + if (customName != null) { + extra.setCustomName(customName); + } + String customDataString = o.getString("ds", null); + if (customDataString != null) { + extra.setAllCustomData(customDataString); + } + o.endScope(); + return extra; + } + + @Override + public void write(IScopedWriter writer, ItemInstanceExtra extra, SerializationContext ctx, String typeId) { + if (extra == null || extra.isEmpty()) { + writer.endScope(); + return; + } + IObjectWriter o = writer.asObject(); + if (extra.isEnchanted()) { + IArrayWriter enchants = o.beginScope("enc").asArray(); + int[][] rawEnchants = extra.getRawEnchants(); + for (int i = 0; i < rawEnchants[0].length; i++) { + enchants.addInt(rawEnchants[0][i]); + enchants.addInt(rawEnchants[1][i]); + } + enchants.endScope(); + } + if (extra.getCustomName() != null) { + o.putString("nm", extra.getCustomName()); + } + if (extra.getAllCustomData() != null) { + o.putString("ds", extra.getAllCustomData()); + } + o.endScope(); + } + }); + + public static void initSaverId() { + // forces class to load + } + + @Override + public String getSerializationTypeId() { + return saverId; + } + + private long ptr; + private Item item; + private boolean isFinalizable = true; + public boolean isFinalizableInstance() { + return isFinalizable; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if (isFinalizable) { + finalizeNative(ptr); // TODO: !!! make extra ptr for java extra instance unique in any case + } + } + + @ApiBinding + public ItemInstanceExtra(long extra) { + if (extra != 0) { + ptr = extra; + } + else { + ptr = constructNew(); + } + + this.item = ITEMS.get(ptr); + } + + @ZoteOnly + public ItemInstanceExtra(Item item) { + ptr = ITEMS.addPointer(item); + this.item = item; + } + + @ApiBinding + public ItemInstanceExtra() { + ptr = constructNew(); + this.item = ITEMS.get(ptr); + } + + @ApiBinding + public ItemInstanceExtra(ItemInstanceExtra extra) { + long val = getValueOrNullPtr(extra); + if (val != 0) { + ptr = constructClone(extra.item); + } + else { + ptr = constructNew(); + } + + this.item = ITEMS.get(ptr); + } + + public static ItemInstanceExtra cloneExtra(ItemInstanceExtra extra) { + long value = getValueOrNullPtr(extra); + return value != 0 ? new ItemInstanceExtra(constructClone(extra.item)) : null; + } + + @ZoteOnly + public Item getItem() { + return item; + } + + @ZoteOnly + public void bind(Item item) { + this.item = item; + this.customData = null; + this.customDataLoaded = false; + } + + public JSONObject asJson() { + if (isEmpty()) { + return null; + } + JSONObject json = new JSONObject(); + try { + int[][] enchants = getRawEnchants(); + int[] ids = enchants[0]; + int[] levels = enchants[1]; + if (ids.length > 0) { + JSONArray enchantsJson = new JSONArray(); + for (int i = 0; i < ids.length; i++) { + JSONObject enchantJson = new JSONObject(); + enchantJson.put("id", ids[i]); + enchantJson.put("l", levels[i]); + enchantsJson.put(enchantJson); + } + json.put("enchants", enchantsJson); + } + String name = getCustomName(); + if (name != null && name.length() > 0) { + json.put("name", name); + } + String data = getAllCustomData(); + if (data != null && data.length() > 0) { + json.put("data", data); + } + } catch (JSONException ignore) { } + return json.length() > 0 ? json : null; + } + + @ApiBinding + public ItemInstanceExtra copy() { + return new ItemInstanceExtra(this); + } + + @ApiBinding + public boolean isEmpty() { + return getValue() == 0; + } + + // ENCHANT + + @ApiBinding + public boolean isEnchanted() { + return item != null && item.hasEnchantments(); + } + + @ApiBinding + public void addEnchant(int type, int level) { + Enchantment enchantment = Enchantment.get(type); + if (enchantment.getName().equals("%enchantment.unknown")) { + Logger.error("ItemInstanceExtra", "Unknown enchantment with id " + type); + return; + } + if (item != null) { + enchantment = Enchantment.getEnchantment(type); + enchantment.setLevel(level, false); + item.addEnchantment(enchantment); + } + } + + @ApiBinding + public int getEnchantLevel(int type) { + return item != null ? item.getEnchantmentLevel(type) : 0; + } + + @ApiBinding + public void removeEnchant(int type) { + if (item != null && item.hasEnchantments()) { + ListTag ench = item.getNamedTag().getList("ench", CompoundTag.class); + for (int i = 0, l = ench.size(); i < l; i++) { + if (ench.get(i).getShort("id") == type) { + ench.remove(i); + return; + } + } + } + } + + @ApiBinding + public void removeAllEnchants() { + if (item != null && item.hasEnchantments()) { + item.getNamedTag().remove("ench"); + } + } + + @ApiBinding + public int getEnchantCount() { + if (item != null && item.hasEnchantments()) { + Enchantment[] enchantments = item.getEnchantments(); + return enchantments.length; + } + return 0; + } + + @ApiBinding + public String getEnchantName(int id, int lvl) { + Enchantment enchantment = item != null ? item.getEnchantment(id) : null; + if (enchantment == null) { + enchantment = Enchantment.get(id); + } + if (!enchantment.getName().equals("%enchantment.unknown")) { + return lvl > 0 ? enchantment.getName() + " " + Enchantment.getLevelString(lvl) : enchantment.getName(); + } + return null; + } + + @ApiBinding + public String getEnchantName(int id) { + return getEnchantName(id, getEnchantLevel(id)); + } + + public int[][] getRawEnchants() { + if (item == null) { + return new int[0][0]; + } + int count = getEnchantCount(); + int ids[] = new int[count]; + int levels[] = new int[count]; + Enchantment[] enchantments = item.getEnchantments(); + for (int i = 0, l = enchantments.length; i < l; i++) { + Enchantment enchantment = enchantments[i]; + ids[i] = enchantment.getId(); + levels[i] = enchantment.getLevel(); + } + return new int[][] { + ids, + levels + }; + } + + @ApiBinding("getEnchants") + public ScriptableObject getEnchantsForScript() { + int[][] enchants = getRawEnchants(); + int[] ids = enchants[0]; + int[] levels = enchants[1]; + + ScriptableObject result = ScriptableObjectHelper.createEmpty(); + for (int i = 0; i < ids.length; i++) { + if (levels[i] > 0) { + result.put(ids[i], result, levels[i]); + } + } + return result; + } + + @ApiBinding + public String getAllEnchantNames() { + StringBuilder str = new StringBuilder(); + + int[][] enchants = getRawEnchants(); + int[] ids = enchants[0]; + int[] levels = enchants[1]; + for (int i = 0; i < ids.length; i++) { + if (levels[i] > 0) { + str.append(getEnchantName(ids[i], levels[i])).append("\n"); + } + } + + return str.toString(); + } + + // CUSTOM NAME & TAG + + @ApiBinding + public String getCustomName() { + return item != null ? item.getCustomName() : ""; + } + + @ApiBinding + public void setCustomName(String name) { + if (item != null) { + item.setCustomName(name); + } + } + + @ApiBinding + public NativeCompoundTag getCompoundTag() { + CompoundTag tag = item != null ? item.getNamedTag() : null; + return tag != null ? new NativeCompoundTag(tag) : null; + } + + @ApiBinding + public void setCompoundTag(NativeCompoundTag tag) { + if (item != null) { + item.setNamedTag(tag.tag); + applyCustomDataJSON(); + } + } + + // CUSTOM DATA + + @ApiBinding + public String getAllCustomData() { + if (item != null) { + CompoundTag tag = item.getNamedTag(); + return tag != null ? tag.getString(ItemUtils.INNER_CORE_TAG_NAME) : null; + } + return null; + } + + @ApiBinding + public void setAllCustomData(String extra) { + customData = new JSONObject(extra); + if (item != null) { + CompoundTag tag = item.getOrCreateNamedTag(); + if (!customData.isEmpty()) { + tag.putString(ItemUtils.INNER_CORE_TAG_NAME, customData.toString()); + } else { + tag.remove(ItemUtils.INNER_CORE_TAG_NAME); + } + item.setNamedTag(tag); + } + } + + @ApiBinding + public void removeCustomData() { + customData = null; + customDataLoaded = false; + customDataDirty = false; + setAllCustomData(null); + } + + private JSONObject customData = null; + private boolean customDataLoaded = false; + private boolean customDataDirty = false; + + private JSONObject getCustomDataJSON() { + if (!customDataLoaded) { + String raw = getAllCustomData(); + if (raw != null) { + try { + customData = new JSONObject(raw); + } catch (JSONException e) { + customData = null; + } + } + customDataLoaded = true; + } + return customData; + } + + private JSONObject getOrCreateCustomDataJSON() { + JSONObject data = getCustomDataJSON(); + if (data == null) { + data = customData = new JSONObject(); + } + return data; + } + + private void applyCustomDataJSON() { + if (customData != null) { + setAllCustomData(customData.toString()); + } else { + setAllCustomData(null); + } + customDataDirty = false; + } + + private ItemInstanceExtra putObject(String name, Object value) { + JSONObject data = getOrCreateCustomDataJSON(); + try { + data.put(name, value); + customDataDirty = true; + } catch (JSONException ignore) {} + return this; + } + + @ApiBinding + public String getString(String name, String fallback) { + JSONObject data = getCustomDataJSON(); + if (data != null) { + return data.optString(name, fallback); + } + return fallback; + } + + @ApiBinding + public String getString(String name) { + return getString(name, null); + } + + @ApiBinding + public ItemInstanceExtra putString(String name, String value) { + return putObject(name, value); + } + + @ApiBinding + public int getInt(String name, int fallback) { + JSONObject data = getCustomDataJSON(); + if (data != null) { + return data.optInt(name, fallback); + } + return fallback; + } + + @ApiBinding + public int getInt(String name) { + return getInt(name, 0); + } + + @ApiBinding + public ItemInstanceExtra putInt(String name, int value) { + return putObject(name, value); + } + + @ApiBinding + public long getLong(String name, long fallback) { + JSONObject data = getCustomDataJSON(); + if (data != null) { + return data.optLong(name, fallback); + } + return fallback; + } + + @ApiBinding + public long getLong(String name) { + return getLong(name, 0); + } + + @ApiBinding + public ItemInstanceExtra putLong(String name, long value) { + return putObject(name, value); + } + + @ApiBinding + public double getFloat(String name, double fallback) { + JSONObject data = getCustomDataJSON(); + if (data != null) { + return data.optDouble(name, fallback); + } + return fallback; + } + + @ApiBinding + public double getFloat(String name) { + return getFloat(name, 0); + } + + @ApiBinding + public ItemInstanceExtra putFloat(String name, double value) { + return putObject(name, value); + } + + @ApiBinding + public boolean getBoolean(String name, boolean fallback) { + JSONObject data = getCustomDataJSON(); + if (data != null) { + return data.optBoolean(name, fallback); + } + return fallback; + } + + @ApiBinding + public boolean getBoolean(String name) { + return getBoolean(name, false); + } + + @ApiBinding + public ItemInstanceExtra putBoolean(String name, boolean value) { + return putObject(name, value); + } + + @ApiBinding("getSerializable") + public Object getScriptableSerializable(String name) { + SerializationContext ctx = new SerializationContext(); + return ScriptableSerializer.scriptableFromJsonString(getString(name), ctx, true); + } + + @ApiBinding("putSerializable") + public ItemInstanceExtra putScriptableSerializable(String name, Object obj) { + SerializationContext ctx = new SerializationContext(); + return putString(name, ScriptableSerializer.scriptableToJsonString(obj, ctx)); + } + + @Override + public String toString() { + return "ItemExtra{json=" + asJson() + "}"; + } + + // LEGACY NATIVE BIND + + private static long constructNew() { + return ITEMS.addPointer(Item.AIR_ITEM.clone()); + } + + public static long constructClone(long ptr) { + return ITEMS.addPointer(ITEMS.get(ptr)); + } + + public static long constructClone(Item ptr) { + return ITEMS.addPointer(ptr.clone()); + } + + private static void finalizeNative(long ptr) { + ITEMS.removePointer(ptr); + } + + // INTERNAL HELPERS + + public long getValue() { + if (customDataDirty) applyCustomDataJSON(); + return item.getOrCreateNamedTag().isEmpty() ? 0 : ptr; + } + + public static ItemInstanceExtra unwrapObject(Object extra) { + if (extra instanceof Wrapper) { + extra = ((Wrapper) extra).unwrap(); + } + if (extra instanceof ItemInstanceExtra) { + return (ItemInstanceExtra) extra; + } + return null; + } + + public static long unwrapValue(Object extra) { + if (extra == null) { + return 0; + } + if (extra instanceof Wrapper) { + extra = ((Wrapper) extra).unwrap(); + } + if (extra instanceof ItemInstanceExtra) { + return ((ItemInstanceExtra) extra).getValue(); + } + if (extra instanceof Number) { + return ((Number) extra).intValue(); + } + return 0; + } + + public static Item unwrapValueItem(Object extra) { + if (extra == null) { + return Item.AIR_ITEM.clone(); + } + if (extra instanceof Wrapper) { + extra = ((Wrapper) extra).unwrap(); + } + if (extra instanceof ItemInstanceExtra) { + return ((ItemInstanceExtra) extra).getItem(); + } + if (extra instanceof Item item) { + return item; + } + return Item.AIR_ITEM.clone(); + } + + public static long getValueOrNullPtr(ItemInstanceExtra extra) { + return extra != null ? extra.getValue() : 0; + } + + public static ItemInstanceExtra getExtraOrNull(long extra) { + return extra != 0 ? new ItemInstanceExtra(extra) : null; + } + + public static ItemInstanceExtra getExtraOrNull(Item extra) { + return extra != null ? new ItemInstanceExtra(extra) : null; + } + + public static ItemInstanceExtra fromJson(JSONObject json) { + if (json == null || json.length() == 0) { + return null; + } + + ItemInstanceExtra extra = new ItemInstanceExtra(); + JSONArray enchants = json.optJSONArray("enchants"); + if (enchants != null) { + for (int i = 0; i < enchants.length(); i++) { + JSONObject enchant = enchants.optJSONObject(i); + extra.addEnchant(enchant.optInt("id"), enchant.optInt("l")); + } + } + String name = json.optString("name"); + if (name != null) { + extra.setCustomName(name); + } + String data = json.optString("data"); + if (data != null) { + extra.setAllCustomData(data); + } + return extra; + } +} diff --git a/src/main/java/org/innercore/icstd/modules/DimensionsModule.java b/src/main/java/org/innercore/icstd/modules/DimensionsModule.java new file mode 100644 index 000000000..5d63de532 --- /dev/null +++ b/src/main/java/org/innercore/icstd/modules/DimensionsModule.java @@ -0,0 +1,54 @@ +package org.innercore.icstd.modules; + +import com.reider745.InnerCoreServer; +import com.reider745.api.ClientOnly; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.DimensionsBindGenBase; + +@ApiBinding("Dimensions") +public class DimensionsModule extends DimensionsBindGenBase { + // ? + + @ApiBinding + public static void setEvaporatesLiquids(int dimensionId, boolean enabled) { + InnerCoreServer.useNotCurrentSupport("Dimension.setEvaporatesLiquids"); + } + + @ApiBinding + public static void setWeatherSeasons(int dimensionId, boolean enabled) { + InnerCoreServer.useNotCurrentSupport("Dimension.setWeatherSeasons"); + } + + @ApiBinding + public static void setHasSkylight(int dimensionId, boolean enabled) { + InnerCoreServer.useNotCurrentSupport("Dimension.setHasSkylight"); + } + + @ApiBinding + public static int getMoonPhase(int dimensionId) { + InnerCoreServer.useNotCurrentSupport("Dimension.getMoonPhase"); + return 0; + } + + @ApiBinding + public static float getMoonBrightness(int dimensionId) { + InnerCoreServer.useNotCurrentSupport("Dimension.getMoonBrightness"); + return 0; + } + + @ApiBinding + @ClientOnly + public static void setShouldRenderStars(int dimensionId, boolean enabled) {} + + @ApiBinding + @ClientOnly + public static void setShouldRenderSun(int dimensionId, boolean enabled) {} + + @ApiBinding + @ClientOnly + public static void setShouldRenderMoon(int dimensionId, boolean enabled) {} + + @ApiBinding + @ClientOnly + public static void setShouldRenderClouds(int dimensionId, boolean enabled) {} +} diff --git a/src/main/java/org/innercore/icstd/modules/GameModule.java b/src/main/java/org/innercore/icstd/modules/GameModule.java new file mode 100644 index 000000000..7790a5def --- /dev/null +++ b/src/main/java/org/innercore/icstd/modules/GameModule.java @@ -0,0 +1,20 @@ +package org.innercore.icstd.modules; + +import com.reider745.api.ClientOnly; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.GameBindGenBase; + +@ApiBinding("Game") +public class GameModule extends GameBindGenBase +{ + @ClientOnly + public static void addWorldToCache(String path) {} + + @ClientOnly + public static void updateWorlds() {} + + @ClientOnly + public static int getWorldsCount() { + return 0; + } +} diff --git a/src/main/java/org/innercore/icstd/modules/ItemModule.java b/src/main/java/org/innercore/icstd/modules/ItemModule.java new file mode 100644 index 000000000..1bbfb438d --- /dev/null +++ b/src/main/java/org/innercore/icstd/modules/ItemModule.java @@ -0,0 +1,877 @@ +package org.innercore.icstd.modules; + +import cn.nukkit.item.Item; +import com.reider745.InnerCoreServer; +import com.reider745.api.ClientOnly; +import com.reider745.api.CustomManager; +import com.reider745.hooks.item.ItemUtils; +import com.reider745.item.CustomItem; +import com.reider745.item.ItemMethod; +import com.reider745.item.NukkitIdConverter; +import dalvik.annotation.optimization.FastNative; +import org.checkerframework.checker.units.qual.A; +import org.innercore.icstd.annotations.ArgOverload; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.Undefined; + +import com.zhekasmirnov.horizon.runtime.logger.Logger; +import com.zhekasmirnov.innercore.api.NativeAPI; +import com.zhekasmirnov.innercore.api.NativeItem; +import com.zhekasmirnov.innercore.api.NativeItemModel; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.commontypes.ItemInstance; +import com.zhekasmirnov.innercore.api.gen.ItemBindGenBase; +import com.zhekasmirnov.innercore.api.log.ICLog; +import com.zhekasmirnov.innercore.api.mod.adaptedscript.AdaptedScriptAPI.Entity; +import com.zhekasmirnov.innercore.api.runtime.Callback; +import com.zhekasmirnov.innercore.api.runtime.other.NameTranslation; +import com.zhekasmirnov.innercore.api.unlimited.IDRegistry; +import com.zhekasmirnov.innercore.mod.resource.ResourcePackManager; + +import org.jetbrains.annotations.Nullable; + +@ApiBinding("Item") +public class ItemModule extends ItemBindGenBase { + @ApiBinding + @Deprecated + public static NativeItem getItemById(int id) { + return NativeItem.getItemById(id); + } + + @ApiBinding + @Deprecated + public static NativeItem getItemById(@Nullable String nameId) { + return getItemById(getNumericId(nameId)); + } + + @ApiBinding + public static int getIntegerIdByNameId(String nameId) { + if(nameId == null) return -1; + + int id = IDRegistry.getCustomItemIdByName(nameId); + if(id != 0) return id; + + Item item = Item.fromString(nameId); + return NukkitIdConverter.getInnerCoreForNukkit(item.getId(), item.getDamage()).id; + } + + @ApiBinding + public static long constructItem(int id, String nameId, String name, String icon, int iconMeta) { + CustomItem.registerItem(nameId, id, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name)); + + return 0;// NOT USE + } + + @ApiBinding + public static long constructArmorItem(int id, String nameId, String name, String iconName, int iconMeta, String texture, int slot, int defense, int durability, float knockbackResist) { + CustomItem.registerArmorItem(nameId, id, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name), + slot, defense, durability, knockbackResist); + + return 0;// NOT USE + } + + @ApiBinding + public static long constructThrowableItem(int id, String nameId, String name, String iconName, int iconMeta) { + CustomItem.registerThrowableItem(nameId, id, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name)); + + return 0;// NOT USE + } + + // TODO: The server might need to know whether the item has the glint effect. + + @ApiBinding + @ClientOnly + public static boolean isGlint(int id, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + return false; + } + + @ApiBinding + @ClientOnly + public static boolean isGlint(String nameId, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + return false; + } + + + @ApiBinding + @ClientOnly + public static int getMaxUseDuration(int id, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + return ItemUtils.get(id, 1, data, ItemInstanceExtra.getExtraOrNull(extra)).getMaxDurability(); + } + + @ApiBinding + @ClientOnly + public static int getMaxUseDuration(String nameId, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + return getMaxUseDuration(getIntegerIdByNameId(nameId), data, extra); + } + + @ApiBinding + public static void addToCreative(int id, @ArgOverload("1") int count, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + CustomItem.addCreative(id, count, data, ItemInstanceExtra.getExtraOrNull(extra)); + } + + @ApiBinding + public static void addToCreative(String nameId, @ArgOverload("1") int count, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + addToCreative(getIntegerIdByNameId(nameId), count, data, extra); + } + + @ApiBinding + public static boolean isFood(int id) { + return CustomItem.isFood(id); + } + + @ApiBinding + public static boolean isFood(String nameId) { + return isFood(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static boolean isArmor(int id) { + return CustomItem.isArmor(id); + } + + @ApiBinding + public static boolean isArmor(String nameId) { + return isArmor(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static boolean isThrowable(int id) { + return CustomItem.isThrowable(id); + } + + @ApiBinding + public static boolean isThrowable(String nameId) { + return isThrowable(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static int getNutrition(int id) { + return CustomItem.getNutrition(id); + } + + @ApiBinding + public static int getNutrition(String nameId) { + return getNutrition(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static int getArmorValue(int id) { + return ItemMethod.getArmorValue(id); + } + + @ApiBinding + public static int getArmorValue(String nameId) { + return getArmorValue(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static boolean isArmorDamageable(int id) { + return ItemMethod.isArmorDamageable(id); + } + + @ApiBinding + public static boolean isArmorDamageable(String nameId) { + return isArmorDamageable(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + @ClientOnly + public static boolean isRequiresIconOverride(int id) { + return false; + } + + @ApiBinding + @ClientOnly + public static boolean isRequiresIconOverride(String nameId) { + return false; + } + + @ApiBinding + @ClientOnly + public static boolean isToolRender(int id) { + return false; + } + + @ApiBinding + @ClientOnly + public static boolean isToolRender(String nameId) { + return false; + } + + @ApiBinding + public static boolean isHandEquipped(int id) { + InnerCoreServer.useNotCurrentSupport("ItemModule.isHandEquipped"); + return false; + } + + @ApiBinding + public static boolean isHandEquipped(String nameId) { + InnerCoreServer.useNotCurrentSupport("ItemModule.isHandEquipped"); + return false; + } + + @ApiBinding + @ClientOnly + public static int getUseAnimation(int id) { + return 0; + } + + @ApiBinding + @ClientOnly + public static int getUseAnimation(String nameId) { + return 0; + } + + @ApiBinding + public static boolean isStackedByData(int id) { + InnerCoreServer.useNotCurrentSupport("ItemModule.isStackedByData"); + return false;// TODO: nukkit not support + } + + @ApiBinding + public static boolean isStackedByData(String nameId) { + return isStackedByData(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static boolean isAllowedInOffhand(int id) { + return ItemMethod.isAllowedInOffhand(id); + } + + @ApiBinding + public static boolean isAllowedInOffhand(String nameId) { + return isAllowedInOffhand(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static boolean isShouldDespawn(int id) { + return ItemMethod.canShouldDespawn(id); + } + + @ApiBinding + public static boolean isShouldDespawn(String nameId) { + return isShouldDespawn(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static boolean isFireResistant(int id) { + return ItemMethod.isFireResistant(id); + } + + @ApiBinding + public static boolean isFireResistant(String nameId) { + return isFireResistant(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static boolean isExplodable(int id) { + return ItemMethod.canExplodable(id); + } + + @ApiBinding + public static boolean isExplodable(String nameId) { + return isExplodable(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + @ClientOnly + public static boolean isMirroredSprite(int id) { + return false; + } + + @ApiBinding + @ClientOnly + public static boolean isMirroredSprite(String nameId) { + return false; + } + + @ApiBinding + public static int getEnchantSlot(int id) { + return ItemMethod.getEnchantSlot(id); + } + + @ApiBinding + public static int getEnchantSlot(String nameId) { + return getEnchantSlot(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static int getEnchantValue(int id) { + InnerCoreServer.useNotCurrentSupport("ItemModule.getEnchantValue"); + return 0; + } + + @ApiBinding + public static int getEnchantValue(String nameId) { + return getEnchantValue(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static int getMaxDamage(int id, @ArgOverload("0") int data) { + return ItemMethod.getMaxDamageForId(id, data); + } + + @ApiBinding + public static int getMaxDamage(String nameId, @ArgOverload("0") int data) { + return ItemMethod.getMaxDamageForId(getIntegerIdByNameId(nameId), data); + } + + @ApiBinding + public static int getCategory(int id) { + return ItemMethod.getCategory(id); + } + + @ApiBinding + public static int getCategory(String nameId) { + return getCategory(getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static int getCreativeCategory(int id) { + return getCategory(id); + } + + @ApiBinding + public static int getCreativeCategory(String nameId) { + return getCategory(nameId); + } + + @ApiBinding + public static boolean isLiquidClip(int id, @ArgOverload("0") int data) { + return ItemMethod.isLiquidClip(id); + } + + @ApiBinding + public static boolean isLiquidClip(String nameId, @ArgOverload("0") int data) { + return isLiquidClip(getIntegerIdByNameId(nameId), data); + } + + @ApiBinding + public static int getMaxStackSize(int id, @ArgOverload("0") int data) { + return ItemMethod.getMaxStackForId(id, data); + } + + @ApiBinding + public static int getMaxStackSize(String nameId, @ArgOverload("0") int data) { + return getMaxStackSize(getIntegerIdByNameId(nameId), data); + } + + @ApiBinding + public static int getMaxStack(int id, @ArgOverload("0") int data) { + return ItemMethod.getMaxStackForId(id, data); + } + + @ApiBinding + public static int getMaxStack(String nameId, @ArgOverload("0") int data) { + return getMaxStackSize(getIntegerIdByNameId(nameId), data); + } + + @ApiBinding + public static String getName(int id, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + return ItemMethod.getNameForId(id, data, ItemInstanceExtra.getExtraOrNull(extra)); + } + + @ApiBinding + public static String getName(String nameId, @ArgOverload("0") int data, @ArgOverload("0") long extra) { + return getName(getIntegerIdByNameId(nameId), data, extra); + } + + @ApiBinding + @ClientOnly + public static void setGlint(int id, boolean enabled) {} + + @ApiBinding + @ClientOnly + public static void setGlint(String nameId, boolean enabled) {} + + @ApiBinding + public static void setToolRender(int id, boolean enabled) {} + + @ApiBinding + public static void setToolRender(String nameId, boolean enabled) {} + + @ApiBinding + public static void setHandEquipped(int id, boolean enabled) { + ItemMethod.setHandEquipped(CustomItem.getItemManager(id), enabled); + } + + @ApiBinding + public static void setHandEquipped(String nameId, boolean enabled) { + setHandEquipped(getIntegerIdByNameId(nameId), enabled); + } + + @ApiBinding + public static void setLiquidClip(int id, boolean enabled) { + ItemMethod.setLiquidClip(CustomManager.getFor(id), enabled); + } + + @ApiBinding + public static void setLiquidClip(String nameId, boolean enabled) { + setLiquidClip(getIntegerIdByNameId(nameId), enabled); + } + + @ApiBinding + @ClientOnly + public static void setUseAnimation(int id, int animation) {} + + @ApiBinding + @ClientOnly + public static void setUseAnimation(String nameId, int animation) {} + + @ApiBinding + public static void setMaxUseDuration(int id, int duration) { + ItemMethod.setMaxUseDuration(CustomManager.getFor(id), duration); + } + + @ApiBinding + public static void setMaxUseDuration(String nameId, int duration) { + setMaxUseDuration(getIntegerIdByNameId(nameId), duration); + } + + @ApiBinding + public static void setMaxDamage(int id, int damage) { + ItemMethod.setMaxDamage(CustomManager.getFor(id), damage); + } + + @ApiBinding + public static void setMaxDamage(String nameId, int damage) { + setMaxDamage(getIntegerIdByNameId(nameId), damage); + } + + @ApiBinding + public static void setMaxStack(int id, int size) { + ItemMethod.setMaxStackSize(CustomManager.getFor(id), size); + } + + @ApiBinding + public static void setMaxStack(String nameId, int size) { + setMaxStack(getIntegerIdByNameId(nameId), size); + } + + @ApiBinding + public static void setMaxStackSize(int id, int size) { + setMaxStack(id, size); + } + + @ApiBinding + public static void setMaxStackSize(String nameId, int size) { + setMaxStack(nameId, size); + } + + @ApiBinding + public static void setStackedByData(int id, boolean enabled) { + InnerCoreServer.useNotCurrentSupport("ItemModule.setStackedByData"); + } + + @ApiBinding + public static void setStackedByData(String nameId, boolean enabled) { + InnerCoreServer.useNotCurrentSupport("ItemModule.setStackedByData"); + } + + @ApiBinding + public static void setAllowedInOffhand(int id, boolean enabled) { + ItemMethod.setAllowedInOffhand(id, enabled); + } + + @ApiBinding + public static void setAllowedInOffhand(String nameId, boolean enabled) { + setAllowedInOffhand(getIntegerIdByNameId(nameId), enabled); + } + + @ApiBinding + public static void setShouldDespawn(int id, boolean enabled) { + ItemMethod.setShouldDespawn(id, enabled); + } + + @ApiBinding + public static void setShouldDespawn(String nameId, boolean enabled) { + setShouldDespawn(getIntegerIdByNameId(nameId), enabled); + } + + @ApiBinding + public static void setFireResistant(int id, boolean enabled) { + ItemMethod.setFireResistant(CustomManager.getFor(id), enabled); + } + + @ApiBinding + public static void setFireResistant(String nameId, boolean enabled) { + setFireResistant(getIntegerIdByNameId(nameId), enabled); + } + + @ApiBinding + public static void setExplodable(int id, boolean enabled) { + ItemMethod.setExplodable(id, enabled); + } + + @ApiBinding + public static void setExplodable(String nameId, boolean enabled) { + setExplodable(getIntegerIdByNameId(nameId), enabled); + } + + @ApiBinding + @ClientOnly + public static void setMirroredSprite(int id, boolean enabled) {} + + @ApiBinding + @ClientOnly + public static void setMirroredSprite(String nameId, boolean enabled) {} + + @ApiBinding + public static void setProperties(int id, String properties) { + InnerCoreServer.useNotCurrentSupport("ItemModule.setProperties"); + } + + @ApiBinding + public static void setProperties(String nameId, String properties) { + setProperties(getIntegerIdByNameId(nameId), properties); + } + + @ApiBinding + @ClientOnly + public static void setRequiresIconOverride(int id, boolean enabled) {} + + @ApiBinding + @ClientOnly + public static void setRequiresIconOverride(String nameId, boolean enabled) {} + + @ApiBinding + public static void setArmorDamageable(int id, boolean enabled) { + ItemMethod.setArmorDamageable(CustomManager.getFor(id), enabled); + } + + @ApiBinding + public static void setArmorDamageable(String nameId, boolean enabled) { + setArmorDamageable(getIntegerIdByNameId(nameId), enabled); + } + + @ApiBinding + public static void setEnchantability(int id, int type, int value) { + ItemMethod.setEnchantability(CustomManager.getFor(id), type, value); + } + + @ApiBinding + public static void setEnchantability(String nameId, int type, int value) { + setEnchantability(getIntegerIdByNameId(nameId), type, value); + } + + //TODO: Nukkit-MOT's getEnchantAbility not used anywhere. + @ApiBinding + public static void setEnchantType(int id, int type, int value) { + setEnchantability(id, type, value); + } + + @ApiBinding + public static void setEnchantType(String nameId, int type, int value) { + setEnchantability(nameId, type, value); + } + + @ApiBinding + public static void addRepairItemId(int id, int repairId) { + ItemMethod.addRepairItemId(CustomManager.getFor(id), repairId); + } + + @ApiBinding + public static void addRepairItemId(int id, String repairId) { + addRepairItemId(id, getIntegerIdByNameId(repairId)); + } + + @ApiBinding + public static void addRepairItemId(String nameId, String repairId) { + addRepairItemId(getIntegerIdByNameId(nameId), getIntegerIdByNameId(repairId)); + } + + @ApiBinding + public static void addRepairItemId(String nameId, int repairId) { + addRepairItemId(getIntegerIdByNameId(nameId), repairId); + } + + @ApiBinding + public static void addRepairItem(int id, int repairId) { + addRepairItemId(id, repairId); + } + + @ApiBinding + public static void addRepairItem(int id, String repairId) { + addRepairItemId(id, getIntegerIdByNameId(repairId)); + } + + @ApiBinding + public static void addRepairItem(String nameId, String repairId) { + addRepairItemId(getIntegerIdByNameId(nameId), getIntegerIdByNameId(repairId)); + } + + @ApiBinding + public static void addRepairItem(String nameId, int repairId) { + addRepairItemId(getIntegerIdByNameId(nameId), repairId); + } + + @ApiBinding + public static void setCreativeCategory(int id, int category) { + ItemMethod.setCreativeCategory(id, category); + } + + @ApiBinding + public static void setCreativeCategory(String nameId, int category) { + ItemMethod.setCreativeCategory(getIntegerIdByNameId(nameId), category); + } + + @ApiBinding + @ClientOnly + public static void setNameOverrideCallbackForced(int id, boolean forced) {} + + @ApiBinding + @ClientOnly + public static void setNameOverrideCallbackForced(String nameId, boolean forced) {} + + @ApiBinding + @ClientOnly + public static void sendCachedNameOverride(int id, int data, String name) {} + + @ApiBinding + @ClientOnly + public static void sendCachedNameOverride(String nameId, int data, String name) {} + + @ApiBinding + public static void addToCreativeGroup(String groupName, String displayName, int id) { + CustomItem.addToCreativeGroup(groupName, id); + } + + @ApiBinding + public static void addToCreativeGroup(String groupName, String displayName, String nameId) { + CustomItem.addToCreativeGroup(groupName, getIntegerIdByNameId(nameId)); + } + + @ApiBinding + public static int getNumericId(@Nullable String nameId) { + if (nameId != null) { + int numericId = IDRegistry.getCustomItemIdByName(nameId); + if (numericId != 0) { + return numericId; + } + } + ICLog.e("InnerCore-ItemRegistry", "Invalid item namedID: " + nameId + ", -1 will be returned", null); + return -1; + } + + @ApiBinding + public static int getNumericId(int id) { + return id; // for compatibility reasons + } + + private static void registerIcon(int id, String iconName, int iconIndex) { + String name = ResourcePackManager.getItemTextureName(iconName, iconIndex); + if (name != null) { + NativeAPI.addTextureToLoad(name); + NativeItemModel.getFor(id, 0).setItemTexturePath(name); + } + // ItemIconSource.instance.registerIcon(id, name); + } + + @ApiBinding + public static NativeItem createItem(int id, String nameId, String name, String iconName, int iconIndex) { + nameId = NativeAPI.convertNameId(nameId); // any name id must be lowercase + if (!ResourcePackManager.isValidItemTexture(iconName, iconIndex)) { + Logger.debug("WARNING", "invalid item icon: " + iconName + " " + iconIndex); + iconName = "missing_icon"; + iconIndex = 0; + } + registerIcon(id, iconName, iconIndex); + constructItem(id, nameId, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name), iconName, iconIndex); + return NativeItem.constructLegacyItemComponent(id, nameId, name); + } + + @ApiBinding + public static NativeItem createArmorItem(int id, String nameId, String name, String iconName, int iconIndex, String texture, int slot, int defense, int durability, float knockbackResist) { + nameId = NativeAPI.convertNameId(nameId); // any name id must be lowercase + if (!ResourcePackManager.isValidItemTexture(iconName, iconIndex)) { + Logger.debug("WARNING", "invalid item icon: " + iconName + " " + iconIndex); + iconName = "missing_icon"; + iconIndex = 0; + } + registerIcon(id, iconName, iconIndex); + constructArmorItem(id, nameId, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name), iconName, iconIndex, texture, slot, defense, durability, (float) knockbackResist); + return NativeItem.constructLegacyArmorComponent(id, nameId, iconName, slot, defense, knockbackResist); + } + + @ApiBinding + public static NativeItem createThrowableItem(int id, String nameId, String name, String iconName, int iconIndex) { + nameId = NativeAPI.convertNameId(nameId); // any name id must be lowercase + if (!ResourcePackManager.isValidItemTexture(iconName, iconIndex)) { + Logger.debug("WARNING", "invalid item icon: " + iconName + " " + iconIndex); + iconName = "missing_icon"; + iconIndex = 0; + } + registerIcon(id, iconName, iconIndex); + constructThrowableItem(id, nameId, NameTranslation.fixUnicodeIfRequired("item_" + nameId, name), iconName, iconIndex); + return NativeItem.constructLegacyThrowableComponent(id, nameId, iconName); + } + + @ApiBinding + public static NativeItem createFoodItem(int id, String nameId, String name, String iconName, int iconIndex, int food) { + NativeItem item = createItem(id, nameId, name, iconName, iconIndex); + String props = "{\"use_animation\":\"eat\",\"use_duration\": 32,\"food\":{\"nutrition\":" + food + + ",\"saturation_modifier\": \"normal\",\"is_meat\": false}, \"components\": {\"minecraft:food\": {\"nutrition\": " + + food + ", \"saturation_modifier\": \"normal\"}}}"; + setProperties(id, props); + setUseAnimation(id, 1); + setMaxUseDuration(id, 32); + return item; + } + + @ApiBinding + public static boolean isGlint(int id, int data, Object extra) { + return isGlint(id, data, ItemInstanceExtra.unwrapValue(extra)); + } + + @ApiBinding + public static boolean isGlint(@Nullable String nameId, int data, Object extra) { + return isGlint(nameId, data, ItemInstanceExtra.unwrapValue(extra)); + } + + @ApiBinding + public static int getMaxUseDuration(int id, int data, Object extra) { + return getMaxUseDuration(id, data, ItemInstanceExtra.unwrapValue(extra)); + } + + @ApiBinding + public static int getMaxUseDuration(@Nullable String nameId, int data, Object extra) { + return getMaxUseDuration(nameId, data, ItemInstanceExtra.unwrapValue(extra)); + } + + @ApiBinding + public static void setCategory(int id, int category) { + setCreativeCategory(id, category); + } + + @ApiBinding + public static void setCategory(@Nullable String nameId, int category) { + setCreativeCategory(nameId, category); + } + + @ApiBinding + public static void addToCreative(int id, int count, int data, Object extra) { + addToCreative(id, count, data, ItemInstanceExtra.unwrapValue(extra)); + } + + @ApiBinding + public static void addToCreative(@Nullable String nameId, int count, int data, Object extra) { + addToCreative(nameId, count, data, ItemInstanceExtra.unwrapValue(extra)); + } + + @ApiBinding + public static void addCreativeGroup(String groupName, String displayName, NativeArray ids) { + for (int offset = 0; offset < ids.getLength(); offset++) { + Object id = ids.get(offset); + if (id instanceof Number) { + addToCreativeGroup(groupName, displayName, ((Number) id).intValue()); + } else if (id instanceof CharSequence || id == null || id == Undefined.instance) { + addToCreativeGroup(groupName, displayName, id == null || id == Undefined.instance ? null : id.toString()); + } else { + throw new IllegalArgumentException("Item.addCreativeGroup received malformed id property at index " + offset); + } + } + } + + @ApiBinding + public static void addRepairItems(int id, NativeArray repairIds) { + for (int offset = 0; offset < repairIds.getLength(); offset++) { + Object repairId = repairIds.get(offset); + if (repairId instanceof Number) { + addRepairItemId(id, ((Number) repairId).intValue()); + } else if (repairId instanceof CharSequence || repairId == null || repairId == Undefined.instance) { + addRepairItemId(id, repairId == null || repairId == Undefined.instance ? null : repairId.toString()); + } else { + throw new IllegalArgumentException("Item.addRepairItems received malformed id property at index " + offset); + } + } + } + + @ApiBinding + public static void addRepairItems(@Nullable String id, NativeArray repairIds) { + for (int offset = 0; offset < repairIds.getLength(); offset++) { + Object repairId = repairIds.get(offset); + if (repairId instanceof Number) { + addRepairItemId(id, ((Number) repairId).intValue()); + } else if (repairId instanceof CharSequence || repairId == null || repairId == Undefined.instance) { + addRepairItemId(id, repairId == null || repairId == Undefined.instance ? null : repairId.toString()); + } else { + throw new IllegalArgumentException("Item.addRepairItems received malformed id property at index " + offset); + } + } + } + + @ApiBinding + public static void addRepairItemIds(int id, NativeArray repairIds) { + addRepairItems(id, repairIds); + } + + @ApiBinding + public static void addRepairItemIds(@Nullable String id, NativeArray repairIds) { + addRepairItems(id, repairIds); + } + + @ApiBinding + public static boolean isValid(int id) { + return NativeAPI.getStringIdAndTypeForIntegerId(id) != null; + } + + @ApiBinding + public static boolean isValid(@Nullable String nameId) { + return getIntegerIdByNameId(nameId) != -1; + } + + @ApiBinding + public static void invokeUseItemOn(int id, int count, int data, Object extra, int x, int y, int z, int side, double vx, double vy, double vz, Object entity) { + NativeAPI.invokeUseItemOn(id, count, data, ItemInstanceExtra.unwrapValueItem(extra), x, y, z, side, (float) vx, (float) vy, (float) vz, Entity.unwrapEntity(entity)); + } + + @ApiBinding + public static void invokeUseItemNoTarget(int id, int count, int data, Object extra) { + NativeAPI.invokeUseItemNoTarget(id, count, data, ItemInstanceExtra.unwrapValueItem(extra)); + } + + + @ApiBinding + public static void overrideCurrentName(String name) { + NativeAPI.overrideItemName(name); + } + + public static final Object DYNAMIC_ICON_LOCK = new Object(); + public static final Object DYNAMIC_NAME_LOCK = new Object(); + + private static boolean isInnerCoreUIOverride = false; + private static String lastIconOverridePath = null; + + @ApiBinding + public static void overrideCurrentIcon(String name, int index) { + synchronized (DYNAMIC_ICON_LOCK) { + if (!isInnerCoreUIOverride) { + NativeAPI.overrideItemIcon(name, index); + } + lastIconOverridePath = ResourcePackManager.getItemTextureName(name, index); + if (lastIconOverridePath != null && !lastIconOverridePath.endsWith(".png")) { + lastIconOverridePath += ".png"; + } + } + } + + @ApiBinding + public static String getLastIconOverridePath() { + return lastIconOverridePath; + } + + @ApiBinding + public static synchronized String getDynamicIconOverride(int id, int count, int data, ItemInstanceExtra extra) { + if (isRequiresIconOverride(id)) { + synchronized (DYNAMIC_ICON_LOCK) { + isInnerCoreUIOverride = true; + lastIconOverridePath = null; + Callback.invokeAPICallback("ItemIconOverride", new ItemInstance(id, count, data, extra), true); + isInnerCoreUIOverride = false; + return lastIconOverridePath; + } + } + return null; + } +} diff --git a/src/main/java/org/innercore/icstd/modules/PlayerModule.java b/src/main/java/org/innercore/icstd/modules/PlayerModule.java new file mode 100644 index 000000000..65a58093f --- /dev/null +++ b/src/main/java/org/innercore/icstd/modules/PlayerModule.java @@ -0,0 +1,59 @@ +package org.innercore.icstd.modules; + +import com.reider745.api.ClientOnly; +import org.innercore.icstd.common.Vec3f; +import org.innercore.icstd.common.script.GenericScriptPos3; + +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.PlayerBindGenBase; + +@ApiBinding("Player") +public class PlayerModule extends PlayerBindGenBase { + public static Vec3f getMoveInputVector() { + float[] vector = new float[3]; + getMoveInputVectorArray(vector); + return new Vec3f(vector[0], vector[1], vector[2]); + } + + @ApiBinding("getMoveInputVector") + public static GenericScriptPos3 getMoveInputVectorForScript() { + float[] vector = new float[3]; + getMoveInputVectorArray(vector); + return new GenericScriptPos3(vector[0], vector[1], vector[2]); + } + + @ClientOnly + public static void getMoveInputVectorArray(float[] output) {} + + @ClientOnly + public static boolean wantsMove() { + return false; + } + + @ClientOnly + public static float getMoveInputVectorX() { + return 0; + } + + @ClientOnly + public static float getMoveInputVectorY() { + return 0; + } + + @ClientOnly + public static float getMoveInputVectorZ() { + return 0; + } + + @ClientOnly + public static void setFov(float fov) {} + + @ClientOnly + public static void resetFov() {} + + @ClientOnly + public static void setViewPerspective(int perspective) {} + + @ClientOnly + public static void resetViewPerspective() {} +} diff --git a/src/main/java/org/innercore/icstd/network/INetPacket.java b/src/main/java/org/innercore/icstd/network/INetPacket.java new file mode 100644 index 000000000..3c66eb6e7 --- /dev/null +++ b/src/main/java/org/innercore/icstd/network/INetPacket.java @@ -0,0 +1,6 @@ +package org.innercore.icstd.network; + +public interface INetPacket { + String getPacketType(); // must be a valid serializer id + void process(NetworkClient fromClient, String meta); +} diff --git a/src/main/java/org/innercore/icstd/network/IPacketListener.java b/src/main/java/org/innercore/icstd/network/IPacketListener.java new file mode 100644 index 000000000..60387a076 --- /dev/null +++ b/src/main/java/org/innercore/icstd/network/IPacketListener.java @@ -0,0 +1,8 @@ +package org.innercore.icstd.network; + +import org.innercore.icstd.serialization.IScopedReader; +import org.innercore.icstd.serialization.SerializationContext; + +public interface IPacketListener { + void process(NetworkClient fromClient, IScopedReader io, SerializationContext ctx, String meta); +} diff --git a/src/main/java/org/innercore/icstd/network/NetworkClient.java b/src/main/java/org/innercore/icstd/network/NetworkClient.java new file mode 100644 index 000000000..a112ed6b1 --- /dev/null +++ b/src/main/java/org/innercore/icstd/network/NetworkClient.java @@ -0,0 +1,11 @@ +package org.innercore.icstd.network; + +import com.zhekasmirnov.apparatus.multiplayer.server.ConnectedClient; +import org.innercore.icstd.annotations.ApiHidden; + +// we can't just move ConnectedClient here, as it is used and not easily stubbed, +// and besides, there is not much to use of its contents in ICSTD, so make a mirror +public class NetworkClient { + final ConnectedClient impl; + @ApiHidden public NetworkClient(ConnectedClient impl) { this.impl = impl; } +} diff --git a/src/main/java/org/innercore/icstd/network/NetworkModule.java b/src/main/java/org/innercore/icstd/network/NetworkModule.java new file mode 100644 index 000000000..3b39721e8 --- /dev/null +++ b/src/main/java/org/innercore/icstd/network/NetworkModule.java @@ -0,0 +1,139 @@ +package org.innercore.icstd.network; + +import com.zhekasmirnov.apparatus.multiplayer.Network; +import com.zhekasmirnov.apparatus.multiplayer.ThreadTypeMarker; +import org.innercore.icstd.annotations.ApiHidden; +import org.innercore.icstd.serialization.IScopedReader; +import org.innercore.icstd.serialization.IScopedWriter; +import org.innercore.icstd.serialization.ObjectSerializer; +import org.innercore.icstd.serialization.SerializationContext; +import org.innercore.internal.serialization.JsonIO; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class NetworkModule { + private static final HashMap> clientPacketListeners = new HashMap<>(); + private static final HashMap> serverPacketListeners = new HashMap<>(); + + private static IScopedWriter sendPacketImpl(boolean isServer, NetworkClient toClient, String name) { + return new JsonIO.ScopedWriter() { + @Override + public void endScope() { + super.endScope(); + Object data = getResult(); + if (isServer) + ThreadTypeMarker.assertServerThread(); + else + ThreadTypeMarker.assertClientThread(); + if (isServer) { + if (toClient == null) { + Network.getSingleton().getServer().sendToAll(name, data); + } else { + toClient.impl.send(name, data); + } + } else { + Network.getSingleton().getClient().send(name, data); + } + } + }; + } + + public List getAllClients() { + return Network.getSingleton().getServer().getConnectedClientsForApi(); + } + + private static void sendPacketImpl(boolean isServer, NetworkClient toClient, INetPacket packet) { + SerializationContext ctx = new SerializationContext(); + String typeId = packet.getPacketType(); + if (typeId == null) { + throw new IllegalStateException("INetPacket type is null"); + } else if (!ObjectSerializer.isTypeRegistered(typeId)) { + throw new IllegalStateException("INetPacket type is not registered: " + typeId); + } + ObjectSerializer.serializeObjectUnpacked(sendPacketImpl(isServer, toClient, typeId), packet, typeId, ctx, true); + } + + public static IScopedWriter sendToServer(String name) { + return sendPacketImpl(false, null, name); + } + + public static IScopedWriter sendToClient(NetworkClient toClient, String name) { + if (toClient == null) throw new IllegalArgumentException("toClient is null"); + return sendPacketImpl(true, toClient, name); + } + + public static IScopedWriter broadcastToClients(String name) { + return sendPacketImpl(true, null, name); + } + + public static void sendToServer(INetPacket packet) { + sendPacketImpl(false, null, packet); + } + + public static void sendToClient(NetworkClient toClient, INetPacket packet) { + if (toClient == null) throw new IllegalArgumentException("toClient is null"); + sendPacketImpl(true, toClient, packet); + } + + public static void broadcastToClients(INetPacket packet) { + sendPacketImpl(true, null, packet); + } + + public static void addClientPacketListener(String name, IPacketListener listener) { + List listeners = clientPacketListeners.get(name); + if (listeners == null) { + clientPacketListeners.put(name, listeners = new ArrayList<>()); + } + listeners.add(listener); + } + + public static void removeClientPacketListener(String name, IPacketListener listener) { + List listeners = clientPacketListeners.get(name); + if (listeners != null) { + listeners.remove(listener); + } + } + + public static void addServerPacketListener(String name, IPacketListener listener) { + List listeners = serverPacketListeners.get(name); + if (listeners == null) { + serverPacketListeners.put(name, listeners = new ArrayList<>()); + } + listeners.add(listener); + } + + public static void removeServerPacketListener(String name, IPacketListener listener) { + List listeners = serverPacketListeners.get(name); + if (listeners != null) { + listeners.remove(listener); + } + } + + @ApiHidden + public static void processPacketImpl(NetworkClient fromClient, String name, Object data, String meta) { + if (ObjectSerializer.getSerializerRecordByTypeId(name) == null) + return; + SerializationContext ctx = new SerializationContext(); + if (data instanceof JSONObject || data instanceof JSONArray || data == null) { + Object maybePacket = ObjectSerializer.deserializeObjectUnpacked(new JsonIO.ScopedReader(data), name, Object.class, ctx, true); + (fromClient != null ? Network.getSingleton().getServerThreadJobExecutor() : Network.getSingleton().getClientThreadJobExecutor()).add(() -> { + List listeners = (fromClient != null ? serverPacketListeners : clientPacketListeners).get(name); + if (listeners != null) { + for (IPacketListener listener : listeners) { + IScopedReader io = new JsonIO.ScopedReader(data); + listener.process(fromClient, io, ctx, meta); + io.forceEndScope(ctx); + } + } + if (maybePacket instanceof INetPacket) { + ((INetPacket) maybePacket).process(fromClient, meta); + } + }); + } + } + +} diff --git a/src/main/java/org/innercore/icstd/region/BlockSource.java b/src/main/java/org/innercore/icstd/region/BlockSource.java new file mode 100644 index 000000000..5ec7509bd --- /dev/null +++ b/src/main/java/org/innercore/icstd/region/BlockSource.java @@ -0,0 +1,985 @@ +package org.innercore.icstd.region; + +import cn.nukkit.Player; +import cn.nukkit.Server; +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockID; +import cn.nukkit.blockentity.BlockEntity; +import cn.nukkit.entity.Entity; +import cn.nukkit.entity.item.EntityItem; +import cn.nukkit.item.Item; +import cn.nukkit.level.Explosion; +import cn.nukkit.level.GlobalBlockPalette; +import cn.nukkit.level.Level; +import cn.nukkit.level.Position; +import cn.nukkit.level.particle.DestroyBlockParticle; +import cn.nukkit.math.BlockVector3; +import cn.nukkit.math.SimpleAxisAlignedBB; +import cn.nukkit.math.Vector3; +import cn.nukkit.nbt.tag.CompoundTag; +import cn.nukkit.network.protocol.PlaySoundPacket; +import cn.nukkit.network.protocol.StopSoundPacket; +import com.reider745.InnerCoreServer; +import com.reider745.api.CallbackHelper; +import com.reider745.api.ClientOnly; +import com.reider745.api.ReflectHelper; +import com.reider745.api.ZoteOnly; +import com.reider745.block.BlockMethods; +import com.reider745.block.BlockStateStorage; +import com.reider745.entity.EntityMethod; +import com.reider745.hooks.item.ItemUtils; +import com.reider745.world.FakeDimensions; +import com.zhekasmirnov.apparatus.adapter.innercore.game.block.BlockBreakResult; +import com.zhekasmirnov.apparatus.adapter.innercore.game.entity.StaticEntity; +import com.zhekasmirnov.apparatus.mcpe.NativeStaticUtils; +import com.zhekasmirnov.apparatus.util.Java8BackComp; +import com.zhekasmirnov.innercore.api.NativeAPI; +import com.zhekasmirnov.innercore.api.NativeCallback; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.innercore.icstd.annotations.ApiHidden; +import org.innercore.icstd.blockentity.BlockEntityRegistry; +import org.innercore.icstd.blockentity.script.ScriptBlockEntity; +import org.innercore.icstd.common.ChunkPos; +import org.innercore.icstd.component.ComponentUtils; +import org.innercore.icstd.component.DestroyBlockListenerComponent; +import org.innercore.icstd.inventory.ItemInstanceExtra; +import com.zhekasmirnov.innercore.api.NativeTileEntity; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.BlockSourceBindGenBase; +import com.zhekasmirnov.innercore.api.log.ICLog; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import com.zhekasmirnov.innercore.api.runtime.Callback; +import org.innercore.icstd.blockentity.BlockEntityComponent; +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.common.script.GenericScriptPos3; +import org.innercore.icstd.inventory.ItemInstance; +import org.innercore.icstd.scene.GameObjectHandle; +import org.innercore.icstd.scene.GameObjectManager; +import org.innercore.icstd.script.ScriptObjectComponent; +import org.innercore.icstd.world.DestroyBlockContext; +import org.jetbrains.annotations.Nullable; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.ScriptableObject; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@ApiBinding("BlockSource") +public class BlockSource extends BlockSourceBindGenBase { + private static final Map defaultBlockSourceForDimensions = new HashMap<>(); + private static final BlockSource serverCallbackBlockSource = new BlockSource(0, false, null); + private static final Object breakBlockSyncLock = new Object(); + + @ApiBinding + public static BlockSource getDefaultForDimension(int dimension) { + synchronized (defaultBlockSourceForDimensions) { + try { + return Java8BackComp.computeIfAbsent(defaultBlockSourceForDimensions, dimension, key -> new BlockSource(dimension)); + } catch (IllegalArgumentException e) { + return null; + } + } + } + + @ApiBinding + public static BlockSource getDefaultForActor(long actor) { + if (!NativeStaticUtils.isExistingEntity(actor)) { + return null; + } + int dimension = NativeAPI.getEntityDimension(actor); + return getDefaultForDimension(dimension); + } + + @ApiBinding + public static BlockSource getCurrentWorldGenRegion() { + Level level = CallbackHelper.getForCurrentThread(); + if(level == null) level = Server.getInstance().getDefaultLevel(); + return level != null ? new BlockSource(level.getId()) : null; + } + + @ApiBinding + @ClientOnly + public static BlockSource getCurrentClientRegion() { + InnerCoreServer.useIncomprehensibleMethod("BlockSource.getCurrentClientRegion()"); + return null; + } + + @ApiBinding + public static BlockSource getFromCallbackPointer(long ptr) { + return new BlockSource(ptr, false); + } + + @ApiBinding + public static BlockSource getFromServerCallbackPointer(long ptr) { + serverCallbackBlockSource.setNewPointer(ptr, false); + return serverCallbackBlockSource; + } + + public static BlockSource getFromServerCallbackPointer(Level level) { + return getDefaultForDimension(FakeDimensions.getFakeIdForLevel(level)); + } + + @ApiBinding + public static void resetDefaultBlockSources() { + ICLog.d("BlockSource", "resetting default block sources"); + synchronized (defaultBlockSourceForDimensions) { + defaultBlockSourceForDimensions.clear(); + } + } + + private static void defDestroy(Level level, Block target) { + BlockEntity blockEntity = level.getBlockEntity(target); + if (blockEntity != null) { + blockEntity.onBreak(); + blockEntity.close(); + level.updateComparatorOutputLevel(target); + } + } + + + private boolean isFinalizable; + + private boolean allowUpdate = true; + private boolean destroyParticles = true; + private int updateType = 3; + private GameObjectManager scene = null; + public Level level; + + public BlockSource(long ptr, boolean isFinalizable) { + Level level = Server.getInstance().getLevel((int) ptr); + if (level == null) { + throw new RuntimeException("cannot pass null pointer to BlockSource constructor"); + } + this.level = level; + } + + private BlockSource(long ptr, boolean isFinalizable, Object nullablePointerTag) { + this.level = Server.getInstance().getLevel((int) ptr); + } + + public BlockSource(int dimension, boolean b1, boolean b2) { + this(FakeDimensions.getLevelForFakeId(dimension).getId(), true); + } + + public BlockSource(int dimension) { + this(dimension, true, false); + } + + public static BlockSource getDefault() { + Level level = Server.getInstance().getDefaultLevel(); + if(level == null) return null; + return new BlockSource(level.getId(), false, false); + } + + @ApiBinding + public boolean isServerSide() { + return true; + } + + @ApiBinding + public boolean canSeeSky(int x, int y, int z) { + return level.canBlockSeeSky(new Vector3(x, y, z)); + } + + @ApiBinding + public int getBiome(int x, int z) { + return level.getBiomeId(x, z); + } + + @ApiBinding + public float getBiomeTemperatureAt(int x, int y, int z) { + InnerCoreServer.useNotCurrentSupport("BlockSource.getBiomeTemperatureAt"); + return 0; + } + + @ApiBinding + public float getBiomeDownfallAt(int x, int y, int z) { + InnerCoreServer.useNotCurrentSupport("BlockSource.getBiomeDownfallAt"); + return 0; + } + + @ApiBinding + public int getLightLevel(int x, int y, int z) { + if (level.isDaytime() && level.canBlockSeeSky(new Vector3(x, y, z))) { + return 15; + } + return level.getBlockLightAt(x, y, z); + } + + @ApiBinding + public int getBlockId(int x, int y, int z) { + if(y >= level.getMinBlockY() && y <= level.getMaxBlockY()) { + return level.getBlock(x, y, z).getId(); + } + return 0; + } + + @ApiBinding + public int getBlockID(int x, int y, int z) { + return this.getBlockId(x, y, z); + } + + @ApiBinding + public int getBlockData(int x, int y, int z) { + if(y >= level.getMinBlockY() && y <= level.getMaxBlockY()) { + return level.getBlockDataAt(x, y, z); + } + return 0; + } + + @ApiBinding + public int getGrassColor(int x, int y, int z) { + InnerCoreServer.useNotCurrentSupport("BlockSource.getBiomeDownfallAt"); + return 0; + } + + @ApiBinding + public float getRainLevel() { + return level.isRaining() ? 1 : 0; + } + + @ApiBinding + public void setRainLevel(float rainLevel) { + level.setRaining(rainLevel != 0); + } + + @ApiBinding + public float getLightningLevel() { + return level.isThundering() ? 1 : 0; + } + + @ApiBinding + public void setLightningLevel(float lightningLevel) { + level.setThundering(lightningLevel != 0); + } + + @ApiBinding + public boolean containsMaterial(float x1, float y1, float z1, float x2, float y2, float z2, int material) { + for(Block block : level.getCollisionBlocks(new SimpleAxisAlignedBB(x1, y1, z1, x2, y2, z2))) { + if(BlockMethods.getMaterial(block.getId()) == material) { + return true; + } + } + + return false; + } + + @ApiBinding + public int getRedstoneSignal(int x, int y, int z) { + return level.getStrongPower(new Vector3(x, y, z)); + } + + @ApiBinding + public void setRedstoneSignal(int x, int y, int z, int level, int delay, int facing) { + InnerCoreServer.useNotCurrentSupport("NativeBlockSource.setRedstoneSignal"); + } + + @ApiBinding + public void setRedstoneSignal(int x, int y, int z, int level, int delay) { + setRedstoneSignal(x, y, z, level, delay, -1); + } + + @ApiBinding + public void setRedstoneSignal(int x, int y, int z, int level) { + setRedstoneSignal(x, y, z, level, 4); + } + + @ApiBinding + public int getDimension() { + return FakeDimensions.getFakeIdForLevel(level); + } + + private static final boolean[] randomTickBlocks = ReflectHelper.getField(Level.class, "randomTickBlocks"); + + @ApiBinding + public void randomTick(int x, int y, int z) { + final Block block = level.getBlock(x, y, z); + + assert randomTickBlocks != null; + + if(randomTickBlocks[block.getId()]) { + block.onUpdate(Level.BLOCK_UPDATE_RANDOM); + } + } + + @ApiBinding + public void setBiome(int chunkX, int chunkZ, int id) { + level.setBiomeId(chunkX, chunkZ, (byte) id); + } + + @ApiBinding + public boolean isChunkLoaded(int chunkX, int chunkZ) { + return level.isChunkLoaded(chunkX, chunkZ); + } + + @ApiBinding + public boolean isChunkLoadedAt(int x, int z) { + return this.isChunkLoaded((int) Math.floor((double) x / 16), (int) Math.floor((double) z / 16)); + } + + @ApiBinding + public int getChunkState(int chunkX, int chunkZ) { + InnerCoreServer.useNotCurrentSupport("BlockSourceMethods.getChunkState(level, chunkX, chunkZ)"); + return 0; + } + + @ApiBinding + public int getChunkStateAt(int x, int z) { + return this.getChunkState((int) Math.floor((double) x / 16), (int) Math.floor((double) z / 16)); + } + + @ApiBinding + public void destroyBlock(int x, int y, int z, boolean drop, int updateType, boolean destroyParticles) { + boolean update = updateType == 3; + Block block = level.getBlock(x, y, z); + if (destroyParticles) { + Map players = level.getChunkPlayers(x >> 4, z >> 4); + level.addParticle(new DestroyBlockParticle(block.add(0.5), block), players.values()); + } + if (drop && block.getId() < 8000) { + Item[] drops = block.getDrops(Item.get(0)); + Vector3 pos = new Vector3(x, y, z); + for (Item item : drops) + level.dropItem(pos, item); + } + defDestroy(level, block); + level.setBlock(x, y, z, Block.get(0), true, update); + } + + @ApiBinding + public void explode(float x, float y, float z, float power, boolean fire) { + // TODO: support fire + final Block block = level.getBlock((int) x, (int) y, (int) z); + final Explosion explosion = new Explosion(new Position(x, y, z, level), power, block); + + explosion.explodeA(); + explosion.explodeB(); + } + + @ApiBinding + public long spawnDroppedItem(float x, float y, float z, int id, int count, int data, long extra) { + EntityItem item = level.dropAndGetItem(new Vector3(x, y, z), ItemUtils.get(id, count, data, ItemInstanceExtra.getExtraOrNull(extra))); + if (item == null) { + return 0; + } + return item.getId(); + } + + @ApiBinding + public long spawnEntity(float x, float y, float z, int type) { + Entity entity = Entity.createEntity(type, new Position(x, y, z, level)); + if (entity != null) { + entity.spawnToAll(); + return EntityMethod.getIdForEntity(entity); + } + return -1; + } + + @ApiBinding + public long spawnNamespacedEntity(float x, float y, float z, String namespaceId, String str2, String extra) { + if (str2 == null) { + return -1; + } + // TODO: Quite BETTER name to identifier conversion! + Entity entity = Entity.createEntity(str2.substring(0, 1).toUpperCase() + str2.substring(1).replace("_", ""), + new Position(x, y, z, level)); + if (entity != null) { + entity.spawnToAll(); + return EntityMethod.getIdForEntity(entity); + } + return -1; + } + + @ApiBinding + public void spawnExpOrbs(float x, float y, float z, int amount) { + if(amount > 0) { + final Position source = new Position(x, y, z, level); + final CompoundTag nbt = Entity.getDefaultNBT(source, new Vector3(0, 0, 0)); + + nbt.putShort("Value", amount); + nbt.putShort("PickupDelay", 10); + + final Entity entity = Entity.createEntity("XpOrb", level.getChunk(source.getChunkX(), source.getChunkZ()), nbt); + if (entity != null) { + entity.spawnToAll(); + } + } + } + + public Block getBlockIdDataAndState(int x, int y, int z) { + return level.getBlock(x, y, z); + } + + public Block getExtraBlockIdDataAndState(int x, int y, int z) { + return level.getBlock(new Vector3(x, y, z), 1); + } + + public void setBlock(int x, int y, int z, int id, int data, boolean allowUpdate, int updateType) { + Block block = level.getBlock(x, y, z); + defDestroy(level, block); + level.setBlock(x, y, z, Block.get(id, data), true, allowUpdate); + } + + public void setBlockByRuntimeId(int x, int y, int z, int runtimeId, boolean allowUpdate, int updateType) { + int[] block = BlockStateStorage.getBlockForRuntimeId(runtimeId); + setBlock(x, y, z, block[0], block[1], allowUpdate, updateType); + } + + public void setExtraBlock(int x, int y, int z, int id, int data, boolean allowUpdate, int updateType) { + level.setBlock(x, y, z, 1, Block.get(id, data), true, allowUpdate); + } + + public void setExtraBlockByRuntimeId(int x, int y, int z, int runtimeId, boolean allowUpdate, int updateType) { + int[] block = BlockStateStorage.getBlockForRuntimeId(runtimeId); + setExtraBlock(x, y, z, block[0], block[1], allowUpdate, updateType); + } + + @ApiBinding + public GameObjectManager getScene() { + if (scene == null) { + scene = GameObjectManager.getForDimension(getDimension()); + } + return scene; + } + + private void setNewPointer(long pointer, boolean isFinalizable) { + this.level = Server.getInstance().getLevel((int) pointer); + this.isFinalizable = isFinalizable; + scene = null; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if (isFinalizable) { + // TODO: watch for possible fix, leave without GC for now + // nativeFinalize(pointer); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BlockSource that = (BlockSource) o; + return level.getId() == that.level.getId(); + } + + @Override + public int hashCode() { + return level.getId() ^ (level.getId() >>> 32); + } + + @ApiBinding + public void setBlockUpdateAllowed(boolean allowed) { + this.allowUpdate = allowed; + } + + @ApiBinding + public boolean getBlockUpdateAllowed() { + return allowUpdate; + } + + @ApiBinding + public void setBlockUpdateType(int type) { + this.updateType = type; + } + + @ApiBinding + public int getBlockUpdateType() { + return updateType; + } + + @ApiBinding + public void setDestroyParticlesEnabled(boolean enabled) { + destroyParticles = enabled; + } + + @ApiBinding + public boolean getDestroyParticlesEnabled() { + return destroyParticles; + } + + @ApiBinding + public BlockState getBlock(int x, int y, int z) { + return new BlockState(getBlockIdDataAndState(x, y, z)); + } + + @ApiBinding + public BlockState getExtraBlock(int x, int y, int z) { + return new BlockState(getExtraBlockIdDataAndState(x, y, z)); + } + + @ApiBinding + public void setBlock(int x, int y, int z, int id, int data) { + setBlock(x, y, z, id, data, allowUpdate, updateType); + } + + @ApiBinding + public void setBlock(int x, int y, int z, int id) { + setBlock(x, y, z, id, 0); + } + + @ApiBinding + public void setBlock(int x, int y, int z, BlockState state) { + int runtimeId = state.getRuntimeId(); + if (runtimeId >= 0) { + setBlockByRuntimeId(x, y, z, runtimeId, allowUpdate, updateType); + } else { + setBlock(x, y, z, state.id, state.data, allowUpdate, updateType); + } + } + + @ApiBinding + public void setExtraBlock(int x, int y, int z, int id, int data) { + setExtraBlock(x, y, z, id, data, allowUpdate, updateType); + } + + @ApiBinding + public void setExtraBlock(int x, int y, int z, int id) { + setExtraBlock(x, y, z, id, 0); + } + + @ApiBinding + public void setExtraBlock(int x, int y, int z, BlockState state) { + int runtimeId = state.getRuntimeId(); + if (runtimeId > 0) { + setExtraBlockByRuntimeId(x, y, z, runtimeId, allowUpdate, updateType); + } else { + setExtraBlock(x, y, z, state.id, state.data, allowUpdate, updateType); + } + } + + public boolean isChunkLoaded(ChunkPos chunkPos) { + return isChunkLoaded(chunkPos.x, chunkPos.z); + } + + @ApiBinding + public int getGrassColor(int x, int z) { + return getGrassColor(x, 64, z); + } + + @ApiBinding + public void addToTickingQueue(int x, int y, int z, BlockState block, int delay, int unknown) { + addToTickingQueue(x, y, z, block.getRuntimeId(), delay, unknown); + } + + @ApiBinding + public void addToTickingQueue(int x, int y, int z, BlockState block, int delay) { + addToTickingQueue(x, y, z, block, delay, 0); + } + + @ApiBinding + public void addToTickingQueue(int x, int y, int z, int delay, int unknown) { + addToTickingQueue(x, y, z, -1, delay, unknown); + } + + @ApiBinding + public void addToTickingQueue(int x, int y, int z, int delay) { + addToTickingQueue(x, y, z, -1, delay, 0); + } + + @ApiBinding + public void destroyBlock(int x, int y, int z, boolean drop) { + destroyBlock(x, y, z, drop, updateType, destroyParticles); + } + + @ApiBinding + public void destroyBlock(int x, int y, int z) { + destroyBlock(x, y, z, false); + } + + @ApiBinding + public void breakBlock(int x, int y, int z, boolean isDropAllowed, long actor, ItemInstance item) { + GenericScriptPos3 coords = new GenericScriptPos3(x, y, z); + BlockState block = getBlock(x, y, z); + Callback.invokeAPICallback("BreakBlock", this, coords, block, isDropAllowed, actor, item); + if (!NativeAPI.isDefaultPrevented() && StaticEntity.exists(actor) && StaticEntity.getDimension(actor) == getDimension()) { + Callback.invokeAPICallback("DestroyBlock", coords, block, actor); + } + destroyBlock(x, y, z, isDropAllowed); + } + + @ApiBinding + public void breakBlock(int x, int y, int z, boolean isDropAllowed, long actor) { + breakBlock(x, y, z, isDropAllowed, actor, StaticEntity.getCarriedItem(actor)); + } + + @ApiBinding + public void breakBlock(int x, int y, int z, boolean isDropAllowed, ItemInstance item) { + breakBlock(x, y, z, isDropAllowed, -1, item); + } + + @ApiBinding + public void breakBlock(int x, int y, int z, boolean isDropAllowed) { + breakBlock(x, y, z, isDropAllowed, -1, new ItemInstance()); + } + + @ApiBinding + public BlockBreakResult breakBlockForResult(int x, int y, int z, long actor, ItemInstance item) { + synchronized (breakBlockSyncLock) { + NativeCallback.startOverrideBlockBreakResult(); + breakBlock(x, y, z, true, actor, item); + return NativeCallback.endOverrideBlockBreakResult(); + } + } + + @ApiBinding + public BlockBreakResult breakBlockForResult(int x, int y, int z, long actor) { + return breakBlockForResult(x, y, z, actor, StaticEntity.getCarriedItem(actor)); + } + + @ApiBinding + public BlockBreakResult breakBlockForResult(int x, int y, int z, ItemInstance item) { + return breakBlockForResult(x, y, z, -1, item); + } + + @ApiBinding + public BlockBreakResult breakBlockForResult(int x, int y, int z) { + return breakBlockForResult(x, y, z, -1, new ItemInstance()); + } + + @ApiBinding + public ScriptableObject breakBlockForJsResult(int x, int y, int z, long actor, ScriptableObject item) { + return breakBlockForResult(x, y, z, actor, new ItemInstance(item)).asScriptable(); + } + + @ApiBinding + public ScriptableObject breakBlockForJsResult(int x, int y, int z, long actor) { + return breakBlockForResult(x, y, z, actor).asScriptable(); + } + + @ApiBinding + public ScriptableObject breakBlockForJsResult(int x, int y, int z, ScriptableObject item) { + return breakBlockForResult(x, y, z, new ItemInstance(item)).asScriptable(); + } + + @ApiBinding + public ScriptableObject breakBlockForJsResult(int x, int y, int z) { + return breakBlockForResult(x, y, z).asScriptable(); + } + + @ApiBinding + public long clip(float x1, float y1, float z1, float x2, float y2, float z2, int mode, float[] output) { + return clip(level, x1, y1, z1, x2, y2, z2, mode, output); + } + + @ApiBinding + public long[] fetchEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, int backCompEntityType, boolean flag) { + return fetchEntitiesInAABB(level, x1, y1, z1, x2, y2, z2, backCompEntityType, flag); + } + + @ApiBinding + public long[] fetchEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, int backCompEntityType) { + return fetchEntitiesInAABB(x1, y1, z1, x2, y2, z2, backCompEntityType, false); + } + + @ApiBinding + public long[] fetchEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2) { + return fetchEntitiesInAABB(x1, y1, z1, x2, y2, z2, 256, false); + } + + @ApiBinding + public long[] fetchEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, String namespace, String type) { + return fetchEntitiesOfTypeInAABB(level, x1, y1, z1, x2, y2, z2, namespace, type); + } + + @ApiBinding + public long[] fetchEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, String fullType) { + String namespace = "minecraft"; + int sep = fullType.indexOf(':'); + if (sep != -1) { + namespace = fullType.substring(0, sep); + fullType = fullType.substring(sep + 1); + } + return fetchEntitiesOfTypeInAABB(x1, y1, z1, x2, y2, z2, namespace, fullType); + } + + @ApiBinding + public NativeArray listEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, int backCompEntityType, boolean flag) { + return ScriptableObjectHelper.longToScriptableArray(fetchEntitiesInAABB(level, x1, y1, z1, x2, y2, z2, backCompEntityType, flag)); + } + + @ApiBinding + public NativeArray listEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2, int backCompEntityType) { + return ScriptableObjectHelper.longToScriptableArray(fetchEntitiesInAABB(x1, y1, z1, x2, y2, z2, backCompEntityType, false)); + } + + @ApiBinding + public NativeArray listEntitiesInAABB(float x1, float y1, float z1, float x2, float y2, float z2) { + return ScriptableObjectHelper.longToScriptableArray(fetchEntitiesInAABB(x1, y1, z1, x2, y2, z2, 256, false)); + } + + @ApiBinding + public NativeArray listEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, String namespace, String type) { + return ScriptableObjectHelper.longToScriptableArray(fetchEntitiesOfTypeInAABB(x1, y1, z1, x2, y2, z2, namespace, type)); + } + + @ApiBinding + public NativeArray listEntitiesOfTypeInAABB(float x1, float y1, float z1, float x2, float y2, float z2, String fullType) { + return ScriptableObjectHelper.longToScriptableArray(fetchEntitiesOfTypeInAABB(x1, y1, z1, x2, y2, z2, fullType)); + } + + @ApiBinding + public long spawnEntity(float x, float y, float z, String str1, String str2, String str3) { + return spawnNamespacedEntity(x, y, z, str1, str2, str3); + } + + @ApiBinding + public long spawnEntity(float x, float y, float z, String type) { + if (type == null || type.length() == 0) { + return -1; + } + String[] parts = type.split(":"); + if (parts.length >= 3) { + return spawnEntity(x, y, z, parts[0], parts[1], parts[2]); + } else if (parts.length == 2) { + return spawnEntity(x, y, z, parts[0], parts[1], ""); + } else if (parts.length == 1) { + return spawnEntity(x, y, z, "minecraft", parts[0], ""); + } + return -1; + } + + @ApiBinding + public long spawnDroppedItem(float x, float y, float z, int id, int count, int data, ItemInstanceExtra extra) { + EntityItem item = level.dropAndGetItem(new Vector3(x, y, z), ItemUtils.get(id, count, data, extra)); + if (item == null) { + return 0; + } + return item.getId(); + } + + @ApiBinding + public long spawnDroppedItem(float x, float y, float z, int id, int count, int data) { + return spawnDroppedItem(x, y, z, id, count, data, null); + } + + @ApiBinding + public long spawnDroppedItem(float x, float y, float z, int id, int count) { + return spawnDroppedItem(x, y, z, id, count, 0, null); + } + + @ApiBinding + public long spawnDroppedItem(float x, float y, float z, int id) { + return spawnDroppedItem(x, y, z, id, 1, 0, null); + } + + public void playSound(float x, float y, float z, String sound, float volume, float pitch, long[] players) { + playSound(level, x, y, z, sound, volume, pitch, players); + } + + @ApiBinding + public void playSound(float x, float y, float z, String sound, float volume, float pitch, NativeArray players) { + playSound(level, x, y, z, sound, volume, pitch, ScriptableObjectHelper.scriptableToLongArray(players)); + } + + @ApiBinding + public void playSoundAtEntity(long entity, String sound, float volume, float pitch) { + float[] position = new float[3]; + NativeAPI.getPosition(entity, position); + playSound(position[0], position[1], position[2], sound, volume, pitch); + } + + @ApiBinding + public void playSoundAtEntity(long entity, String sound, float volume) { + playSoundAtEntity(entity, sound, volume, 1.0f); + } + + @ApiBinding + public void playSoundAtEntity(long entity, String sound) { + playSoundAtEntity(entity, sound, 1.0f, 1.0f); + } + + public void playSoundAtEntity(long entity, String sound, float volume, float pitch, long[] players) { + float[] position = new float[3]; + NativeAPI.getPosition(entity, position); + playSound(position[0], position[1], position[2], sound, volume, pitch, players); + } + + @ApiBinding + public void playSoundAtEntity(long entity, String sound, float volume, float pitch, NativeArray players) { + playSoundAtEntity(entity, sound, volume, pitch, ScriptableObjectHelper.scriptableToLongArray(players)); + } + + public void stopSound(String sound, long[] players) { + stopSound(level, sound, players); + } + + @ApiBinding + public void stopSound(String sound, NativeArray players) { + stopSound(level, sound, ScriptableObjectHelper.scriptableToLongArray(players)); + } + + private static final Int2ObjectMap TILES_TYPES = new Int2ObjectOpenHashMap<>(); + + static { + TILES_TYPES.put(BlockID.CHEST, BlockEntity.CHEST); + TILES_TYPES.put(BlockID.TRAPPED_CHEST, BlockEntity.CHEST); + TILES_TYPES.put(BlockID.ENDER_CHEST, BlockEntity.ENDER_CHEST); + TILES_TYPES.put(BlockID.FURNACE, BlockEntity.FURNACE); + TILES_TYPES.put(BlockID.BLAST_FURNACE, BlockEntity.BLAST_FURNACE); + TILES_TYPES.put(BlockID.SMOKER, BlockEntity.SMOKER); + TILES_TYPES.put(BlockID.MONSTER_SPAWNER, BlockEntity.MOB_SPAWNER); + TILES_TYPES.put(BlockID.ENCHANT_TABLE, BlockEntity.ENCHANT_TABLE); + TILES_TYPES.put(BlockID.ITEM_FRAME_BLOCK, BlockEntity.ITEM_FRAME); + TILES_TYPES.put(BlockID.HOPPER_BLOCK, BlockEntity.HOPPER); + TILES_TYPES.put(BlockID.DROPPER, BlockEntity.DROPPER); + } + + + @ApiBinding("getBlockEntity") // compatibility - scripts expect this to return only native block entities + public NativeTileEntity getNativeBlockEntity(int x, int y, int z) { + BlockEntity entity = level.getBlockEntity(new BlockVector3(x, y, z)); + + // TODO: Некоторые моды просто не знают, что блок может быть, а BlockEntity может и не быть(например StorageInterface) + if(entity == null){ + final String type = TILES_TYPES.get(level.getBlockIdAt(x, y, z)); + if(type != null){ + entity = BlockEntity.createBlockEntity(type, new Position(x, y, z).setLevel(level), new CompoundTag(type) + .putString("id", type) + .putInt("x", x) + .putInt("y", y) + .putInt("z", z)); + } + } + + return entity != null ? new NativeTileEntity(entity) : null; + } + + public BlockEntityComponent getBlockEntity(BlockPos pos) { + BlockEntityComponent blockEntityFromScene = getScene().queryAtBlock(pos, BlockEntityComponent.class).one(); + if (blockEntityFromScene != null) + return blockEntityFromScene; + return getNativeBlockEntity(pos.x, pos.y, pos.z); + } + + public BlockEntityComponent getBlockEntity(int x, int y, int z) { + return getBlockEntity(new BlockPos(x, y, z)); + } + + public void addToTickingQueue(int x, int y, int z, int runtimeId, int delay, int unknown_int) { + InnerCoreServer.useNotCurrentSupport("BlockSourceMethods.addToTickingQueue"); + } + + @ApiBinding + public void playSound(float x, float y, float z, String sound, float volume, float pitch) { + PlaySoundPacket packet = new PlaySoundPacket(); + + packet.name = sound; + packet.volume = volume; + packet.pitch = pitch; + packet.x = (int) x; + packet.y = (int) y; + packet.z = (int) z; + + level.addChunkPacket(packet.x >> 4, packet.z >> 4, packet); + } + + @ApiBinding + public void stopSound(String sound) { + StopSoundPacket packet = new StopSoundPacket(); + + packet.name = sound; + + Server.broadcastPacket(level.getPlayers().values(), packet); + } + + @ApiBinding("getBlockEntityAny") + public Object getBlockEntityForScript(int x, int y, int z) { + Object blockEntity = getBlockEntity(x, y, z); + if (blockEntity instanceof ScriptObjectComponent) + blockEntity = ((ScriptObjectComponent) blockEntity).getScriptObject(); + return blockEntity; + } + + @ApiBinding("getBlockEntityNotNative") + public Object getBlockEntityNotNativeForScript(int x, int y, int z) { + Object blockEntity = getBlockEntity(x, y, z); + if (blockEntity instanceof NativeTileEntity) + return null; + if (blockEntity instanceof ScriptObjectComponent) + blockEntity = ((ScriptObjectComponent) blockEntity).getScriptObject(); + return blockEntity; + } + + public BlockEntityComponent getOrAddBlockEntity(BlockPos pos) { + BlockEntityComponent existing = getBlockEntity(pos); + if (existing != null || !isServerSide()) + return existing; + return BlockEntityRegistry.createForBlockAndPlaceAtScene(getScene(), getBlockID(pos.x, pos.y, pos.z), pos); + } + + public BlockEntityComponent getOrAddBlockEntity(int x, int y, int z) { + return getOrAddBlockEntity(new BlockPos(x, y, z)); + } + + @ApiBinding("getOrAddBlockEntity") + public Object getOrAddBlockEntityForScript(int x, int y, int z) { + Object blockEntity = getOrAddBlockEntity(x, y, z); + if (blockEntity instanceof ScriptObjectComponent) + blockEntity = ((ScriptObjectComponent) blockEntity).getScriptObject(); + return blockEntity; + } + + public void destroyBlockEntity(BlockPos pos, @Nullable DestroyBlockContext ctx) { + if (!isServerSide()) { + throw new IllegalStateException("destroyBlockEntity is only allowed on server side"); + } + if (ctx != null) { + ctx.pos = pos; + } + BlockEntityComponent blockEntity = getScene().queryAtBlock(pos, BlockEntityComponent.class).one(); + if (blockEntity == null) { + return; + } + if (ctx != null) { + ComponentUtils.safeCallBlockDestroyedFor(blockEntity, this, pos, ctx); + } + } + + private static long clip(Level level, float x1, float y1, float z1, float x2, float y2, float z2, int mode, float[] joutput) { + InnerCoreServer.useNotCurrentSupport("BlockSourceMethods.clip(level, x1, y1, z1, x2, y2, z2, mode, joutput)"); + return 0; + } + + private static void playSound(Level level, float x, float y, float z, String sound, float volume, float pitch, long[] players) { + + } + + private static void stopSound(Level level, String sound, long[] players) { + + } + + private static Entity[] fetchEntitiesInAABB(Level level, float x1, float y1, float z1, float x2, float y2, float z2) { + return level.getCollidingEntities(new SimpleAxisAlignedBB(x1, y1, z1, x2, y2, z2)); + } + + private static long[] fetchEntitiesInAABB(Level level, float x1, float y1, float z1, float x2, float y2, float z2, int backCompEntityType, boolean blacklist) { + return Arrays.stream(fetchEntitiesInAABB(level, x1, y1, z1, x2, y2, z2)) + .filter(entity -> { + if (backCompEntityType == 256) { + return true; + } + int entityType = EntityMethod.getEntityTypeDirect(entity); + return blacklist != (entityType == backCompEntityType); + }) + .mapMultiToLong((entity, consumer) -> consumer.accept(EntityMethod.getIdForEntity(entity))) + .toArray(); + } + + private static long[] fetchEntitiesOfTypeInAABB(Level level, float x1, float y1, float z1, float x2, float y2, float z2, String namespace, String name) { + return Arrays.stream(fetchEntitiesInAABB(level, x1, y1, z1, x2, y2, z2)) + .filter(entity -> entity.getName().equalsIgnoreCase(name)) + .mapMultiToLong((entity, consumer) -> consumer.accept(EntityMethod.getIdForEntity(entity))) + .toArray(); + } + + @ApiBinding + @ZoteOnly + public void setRespawnCoords(int x, int y, int z) { + level.setSpawnLocation(new Vector3(x, y, z)); + } +} diff --git a/src/main/java/org/innercore/icstd/region/BlockState.java b/src/main/java/org/innercore/icstd/region/BlockState.java new file mode 100644 index 000000000..bc2d800c1 --- /dev/null +++ b/src/main/java/org/innercore/icstd/region/BlockState.java @@ -0,0 +1,361 @@ +package org.innercore.icstd.region; + +import cn.nukkit.block.Block; +import cn.nukkit.block.BlockMeta; +import cn.nukkit.level.GlobalBlockPalette; +import com.reider745.InnerCoreServer; +import com.reider745.block.BlockStateStorage; +import com.reider745.hooks.block.BlockPalette; +import com.zhekasmirnov.apparatus.minecraft.enums.EnumsContainer; +import com.zhekasmirnov.apparatus.minecraft.enums.GameEnums; +import com.zhekasmirnov.apparatus.minecraft.version.MinecraftVersions; +import com.zhekasmirnov.apparatus.util.Java8BackComp; +import com.zhekasmirnov.innercore.api.NativeCallback; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.BlockStateBindGenBase; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +import java.util.HashMap; +import java.util.Map; + +@ApiBinding +public class BlockState extends BlockStateBindGenBase { + private static final HashMap ALL_STATES = new HashMap<>(); + private static final HashMap VANILLA_STATES = new HashMap<>(); + private static final HashMap CAPACITY_STATES = new HashMap<>(); + + static { + final EnumsContainer.Scope scope = GameEnums.getSingleton().getOrAddContainerForVersion(MinecraftVersions.getCurrent()) + .getScope("block_states"); + + for(String name : scope.getAllEnumNames()) { + final int id = ((Number) scope.getEnum(name)).intValue(); + + ALL_STATES.put(name, id); + VANILLA_STATES.put(name, id); + } + } + + public static int STATE_ID = 114; + + @ApiBinding + public static int registerBlockState(String name, int capacity) { + if(VANILLA_STATES.containsKey(name)) + throw new RuntimeException("You can't create a state with a vanilla name"); + + if(capacity <= 0) + throw new RuntimeException("capacity state <= 0"); + + final int id = Java8BackComp.computeIfAbsent(ALL_STATES, name, n -> STATE_ID++); + final Integer maxCapacity = CAPACITY_STATES.get(id); + + if(maxCapacity == null) { + registerBlockState(name, id, capacity); + CAPACITY_STATES.put(id, capacity); + } else if(maxCapacity < capacity) { + setBlockStateCapacity(id, capacity); + } + + return id; + } + + @ApiBinding + public static int getIdByName(String name) { + return ALL_STATES.getOrDefault(name, -1); + } + + @ApiBinding + public static String getNameById(int id) { + for(String name : ALL_STATES.keySet()) + if(ALL_STATES.get(name) == id) + return name; + return null; + } + + @ApiBinding + public static String[] getAllStates() { + return ALL_STATES.keySet().toArray(new String[0]); + } + + @ApiBinding + public static native int getBlockStateCapacity(int id); + + @ApiBinding public final int id; + @ApiBinding public final int data; + private int cachedRuntimeId = -2; + private int cachedRuntimeIdGeneration = 0; + + private int[] rawStates = null; + private Map states = null; + private Map namedStates = null; + + @ApiBinding + public BlockState(int id, int data) { + super(); + this.id = id; + this.data = data; + getRuntimeId(); + } + + public BlockState(int id, Map map) { + super(); + this.id = id; + + rawStates = new int[map.size() * 2]; + int index = 0; + for (Map.Entry entry : map.entrySet()) { + Object key0 = entry.getKey(); + int key = -1; + if (key0 instanceof Integer) { + key = (int) key0; + } else if (key0 instanceof CharSequence) { + key = getIdByName(key0.toString()); + } + if (key == -1) { + continue; + } + rawStates[index++] = key; + rawStates[index++] = entry.getValue(); + } + + this.cachedRuntimeId = runtimeIdByStates(id, rawStates); + this.data = getDataFromRuntimeId(cachedRuntimeId); + this.cachedRuntimeIdGeneration = NativeCallback.currentGlobalWorldGeneration; + } + + private BlockState(int id, int[] rawStates) { + super(); + this.id = id; + this.rawStates = rawStates; + this.cachedRuntimeId = runtimeIdByStates(id, rawStates); + this.cachedRuntimeIdGeneration = NativeCallback.currentGlobalWorldGeneration; + this.data = getDataFromRuntimeId(cachedRuntimeId); + } + + protected BlockState(Block idDataAndState) { + super(); + + this.id = idDataAndState.getId(); + this.data = idDataAndState.getDamage(); + this.cachedRuntimeId = GlobalBlockPalette.getOrCreateRuntimeId(InnerCoreServer.PROTOCOL, id, data); + this.cachedRuntimeIdGeneration = NativeCallback.currentGlobalWorldGeneration; + } + + BlockState(long idDataAndState) { + long idData = idDataAndState & (long) 0xFFFFFFFF; + this.id = (int) (idData >> (long) 16) & 0xFFFF; + this.data = (int) (idData & (long) 0xFFFF); + this.cachedRuntimeId = (int) (idDataAndState >> (long) 32); + this.cachedRuntimeIdGeneration = NativeCallback.currentGlobalWorldGeneration; + } + + protected BlockState(long idDataAndState, Object stub) { + this(idDataAndState); + } + + public BlockState(long ptr, int refCnt, Object stub) { + super(); + throw new UnsupportedOperationException(); + } + + private static Map scriptableToStateMap(Scriptable scriptable) { + Map result = new HashMap<>(); + for (Object key0 : scriptable.getIds()) { + String key = key0.toString(); + + int state = getIdByName(key); + if (state == -1) { + try { + state = Integer.parseInt(key); + } catch (NumberFormatException e) { + continue; + } + } + + Object value = scriptable.get(key, scriptable); + result.put(state, value instanceof Number ? ((Number) value).intValue() : 0); + } + return result; + } + + @ApiBinding + public BlockState(int id, Scriptable scriptable) { + this(id, scriptableToStateMap(scriptable)); + } + + @ApiBinding + public static BlockState parse(Object o, boolean nullable) { + if (o instanceof BlockState) + return (BlockState) o; + if (o instanceof Scriptable) { + Scriptable s = (Scriptable) o; + int id = ScriptableObjectHelper.getIntProperty(s, "id", 0); + Scriptable states = ScriptableObjectHelper.getScriptableProperty(s, "states", null); + return states != null ? new BlockState(id, states) : new BlockState(id, ScriptableObjectHelper.getIntProperty(s, "data", 0)); + } + return nullable ? null : new BlockState(0, 0); + } + + @ApiBinding + public int getId() { + return id; + } + + @ApiBinding + public int getData() { + return data; + } + + @ApiBinding + public int getRuntimeId() { + if (cachedRuntimeId == -2 || NativeCallback.currentGlobalWorldGeneration != cachedRuntimeIdGeneration) { + if (rawStates != null) { + cachedRuntimeId = runtimeIdByStates(id, rawStates); + } else { + cachedRuntimeId = getRuntimeIdFromIdData(id, data); + } + cachedRuntimeIdGeneration = NativeCallback.currentGlobalWorldGeneration; + } + return cachedRuntimeId; + } + + @ApiBinding + public boolean isValidState() { + return getRuntimeId() != -1; + } + + @ApiBinding + public int getState(int state) { + int runtimeId = getRuntimeId(); + return runtimeId > 0 ? getStateFromId(runtimeId, state) : -1; + } + + @ApiBinding + public boolean hasState(int state) { + return getState(state) != -1; + } + + @ApiBinding + public BlockState addState(int state, int value) { + Map mStates = new HashMap<>(getStates()); + mStates.put(state, value); + return new BlockState(id, mStates); + } + + @ApiBinding + public BlockState addStatesMap(Map states) { + Map mStates = new HashMap<>(getStates()); + mStates.putAll(states); + return new BlockState(id, mStates); + } + + @ApiBinding + public BlockState addStates(Scriptable states) { + return addStatesMap(scriptableToStateMap(states)); + } + + @ApiBinding + public Map getStates() { + if (states == null) { + states = new HashMap<>(); + int runtimeId = getRuntimeId(); + if (runtimeId >= 0) { + rawStates = rawStates != null ? rawStates : getAllStatesFromId(runtimeId); + for (int i = 0; i < rawStates.length / 2; i++) { + states.put(rawStates[i * 2], rawStates[i * 2 + 1]); + } + } + } + return states; + } + + @ApiBinding + public Map getNamedStates() { + if (namedStates == null) { + namedStates = new HashMap<>(); + Map states = getStates(); + for (Map.Entry entry : states.entrySet()) { + namedStates.put(getNameById(entry.getKey()), entry.getValue()); + } + } + return namedStates; + } + + @ApiBinding + public ScriptableObject getStatesScriptable() { + Map states = getStates(); + ScriptableObject result = ScriptableObjectHelper.createEmpty(); + for (Map.Entry entry : states.entrySet()) { + result.put(entry.getKey().toString(), result, entry.getValue()); + } + return result; + } + + @ApiBinding + public ScriptableObject getNamedStatesScriptable() { + Map states = getNamedStates(); + ScriptableObject result = ScriptableObjectHelper.createEmpty(); + for (Map.Entry entry : states.entrySet()) { + result.put(entry.getKey(), result, entry.getValue()); + } + return result; + } + + @Override + public String toString() { + return "BlockState{id=" + id + ", data=" + data + ", runtimeId=" + getRuntimeId() + ", states=" + getNamedStates() + "}"; + } + + @ApiBinding + @Override + public boolean equals(Object object) { + if (this == object) return true; + if (object == null || getClass() != object.getClass()) return false; + + BlockState that = (BlockState) object; + + if (id != that.id) return false; + if (data != that.data) return false; + if (getRuntimeId() != that.getRuntimeId()) return false; + return getStates().equals(that.getStates()); + } + + private static void registerBlockState(String name, int id, int capacity) { + BlockPalette.registerBlockState(name, id, capacity); + } + + private static void setBlockStateCapacity(int id, int capacity) { + BlockPalette.setCapacityForId(id, capacity); + } + + + private static int getStateFromId(int runtimeId, int state){ + return BlockStateStorage.getStateFromId(runtimeId, state); + } + private static int[] getAllStatesFromId(int runtimeId){ + return BlockStateStorage.getAllStatesFromId(runtimeId); + } + + @ApiBinding + public static int runtimeIdByStates(int id, int[] statesAndValues){ + return BlockStateStorage.runtimeIdByStates(id, statesAndValues); + } + + @ApiBinding + public static int getIdFromRuntimeId(int runtimeId){ + InnerCoreServer.useIncomprehensibleMethod("getIdFromRuntimeId"); + return 0; + } + + @ApiBinding + public static int getDataFromRuntimeId(int runtimeId){ + return BlockStateStorage.getDataForRuntimeId(runtimeId); + } + + @ApiBinding + public static int getRuntimeIdFromIdData(int id, int data) { + return GlobalBlockPalette.getOrCreateRuntimeId(InnerCoreServer.PROTOCOL, id, data); + } +} diff --git a/src/main/java/org/innercore/icstd/scene/CreateObjectComponent.java b/src/main/java/org/innercore/icstd/scene/CreateObjectComponent.java new file mode 100644 index 000000000..e263923e1 --- /dev/null +++ b/src/main/java/org/innercore/icstd/scene/CreateObjectComponent.java @@ -0,0 +1,7 @@ +package org.innercore.icstd.scene; + +import org.innercore.icstd.component.ComponentUtils; + +public interface CreateObjectComponent { + void createObject(GameObjectHandle handle); +} diff --git a/src/main/java/org/innercore/icstd/scene/FinalizeObjectComponent.java b/src/main/java/org/innercore/icstd/scene/FinalizeObjectComponent.java new file mode 100644 index 000000000..934088f4b --- /dev/null +++ b/src/main/java/org/innercore/icstd/scene/FinalizeObjectComponent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.scene; + +public interface FinalizeObjectComponent { + void finalizeObject(); +} diff --git a/src/main/java/org/innercore/icstd/scene/GameObjectHandle.java b/src/main/java/org/innercore/icstd/scene/GameObjectHandle.java new file mode 100644 index 000000000..c5cbc65dc --- /dev/null +++ b/src/main/java/org/innercore/icstd/scene/GameObjectHandle.java @@ -0,0 +1,89 @@ +package org.innercore.icstd.scene; + +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.GameObjectHandleBindGenBase; +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.common.script.GenericScriptPos3; +import org.innercore.icstd.script.ScriptObjectComponent; + + +@ApiBinding +public class GameObjectHandle extends GameObjectHandleBindGenBase { + final GameObjectManager scene; + final long rawHandle; + + GameObjectHandle(GameObjectManager scene, long handle) { + this.scene = scene; + this.rawHandle = handle; + } + + @ApiBinding + public GameObjectHandle placeAsGlobal() { + scene.placeAsGlobal(rawHandle); + return this; + } + + @ApiBinding + public GameObjectManager getScene() { + return scene; + } + + public GameObjectHandle placeAtBlockPos(BlockPos pos) { + scene.placeAtBlockPos(rawHandle, pos); + return this; + } + + @ApiBinding + public GameObjectHandle placeAtBlockPos(int x, int y, int z) { + return placeAtBlockPos(new BlockPos(x, y, z)); + } + + @ApiBinding + public GameObjectHandle setObject(Object object) { + scene.setObject(rawHandle, object); + return this; + } + + @ApiBinding + public boolean destroyNow() { return scene.destroyNow(rawHandle); } + + @ApiBinding + public void destroyDeferred() { scene.destroyDeferred(rawHandle); } + + public Object discard() { return scene.discard(rawHandle); } + + @ApiBinding("discard") + public Object discardForScript() { + Object discarded = discard(); + return discarded instanceof ScriptObjectComponent ? ((ScriptObjectComponent) discarded).getScriptObject() : discarded; + } + + @ApiBinding + public boolean isAlive() { return scene.isAlive(rawHandle); } + + @ApiBinding + public boolean isLoaded() { return scene.isLoaded(rawHandle); } + + public BlockPos getBlockPos() { return scene.getBlockPos(rawHandle); } + + @ApiBinding("getBlockPos") + public GenericScriptPos3 getBlockPosForScript() { return new GenericScriptPos3(getBlockPos()); } + + public Object getObject() { return scene.getObject(rawHandle); } + + @ApiBinding("getObject") + public Object getObjectForScript() { + Object object = scene.getObject(rawHandle); + return object instanceof ScriptObjectComponent ? ((ScriptObjectComponent) object).getScriptObject() : object; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) return false; + GameObjectHandle that = (GameObjectHandle) object; + return rawHandle == that.rawHandle; + } + + @Override + public int hashCode() { return (int) rawHandle; } +} diff --git a/src/main/java/org/innercore/icstd/scene/GameObjectManager.java b/src/main/java/org/innercore/icstd/scene/GameObjectManager.java new file mode 100644 index 000000000..8b310d255 --- /dev/null +++ b/src/main/java/org/innercore/icstd/scene/GameObjectManager.java @@ -0,0 +1,962 @@ +package org.innercore.icstd.scene; + +import org.jetbrains.annotations.Nullable; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.GameObjectManagerBindGenBase; +import com.zhekasmirnov.innercore.api.log.ICLog; +import com.zhekasmirnov.innercore.api.runtime.saver.world.WorldDataScopeRegistry; +import org.innercore.icstd.common.BlockPos; +import org.innercore.icstd.common.ChunkAABB; +import org.innercore.icstd.common.ChunkPos; +import org.innercore.icstd.component.*; +import org.innercore.icstd.region.BlockSource; +import org.innercore.icstd.serialization.*; + +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +@ApiBinding +public class GameObjectManager extends GameObjectManagerBindGenBase { + private static final int STATE_NOT_LOADED = 0; + private static final int STATE_LAZY_LOADED = 1; + private static final int STATE_LOADED = 2; + + private final String uniqueName; + private int dimensionId = -1; + private boolean isServerSide = true; + private boolean isDefaultScene = false; + private boolean isScopeLoaded = false; + + public GameObjectManager(String uniqueName) { + this.uniqueName = uniqueName; + } + + public String getUniqueName() { + return uniqueName; + } + + public GameObjectManager setDimensionId(int dimensionId) { + this.dimensionId = dimensionId; + return this; + } + + public GameObjectManager setServerSide(boolean isServerSide) { + this.isServerSide = isServerSide; + return this; + } + + GameObjectManager setDefaultScene(boolean isDefaultScene) { + this.isDefaultScene = isDefaultScene; + return this; + } + + public int getDimensionId() { + return dimensionId; + } + + public boolean isServerSide() { + return isServerSide; + } + + public boolean isDefaultScene() { + return isDefaultScene; + } + + // ADD/UPDATE + + + private static final AtomicLong nextUniqueHandleValue = new AtomicLong(1); + + @ApiBinding + public GameObjectHandle create(Object object) { + GameObjectHandle handle = new GameObjectHandle(this, nextUniqueHandleValue.getAndAdd(1)); + int slotIdx = allocateSlot(); + handleToSlotIdx.put(handle.rawHandle, slotIdx); + slots[slotIdx].handle = handle.rawHandle; + setSlotObject(slotIdx, object); + markObjectForUpdate(slotIdx); + return handle; + } + + void placeAsGlobal(long rawHandle) { + updateObjectPosImpl(rawHandle, slot -> { + slot.posType = POS_TYPE_GLOBAL; + }); + } + + void placeAtBlockPos(long rawHandle, BlockPos pos) { + updateObjectPosImpl(rawHandle, slot -> { + slot.posType = POS_TYPE_BLOCK; + slot.x = pos.x; + slot.y = pos.y; + slot.z = pos.z; + }); + } + + private void updateObjectPosImpl(long rawHandle, Consumer slotConsumer) { + Integer slotIdxOpt = handleToSlotIdx.get(rawHandle); + if (slotIdxOpt == null) { + return; + } + int slotIdx = slotIdxOpt; + ObjectSlot slot = slots[slotIdx]; + BlockPos prevBlockPos = slot.getBlockPos(); + ChunkAABB prevAABB = slot.getChunkAABB(); + slotConsumer.accept(slot); + updateObjectAABB(slotIdx, prevAABB, slot.getChunkAABB()); + updateObjectBlockPos(slotIdx, prevBlockPos, slot.getBlockPos()); + markObjectForUpdate(slotIdx); + } + + Object discard(long rawHandle) { + Integer slotIdxOpt = handleToSlotIdx.remove(rawHandle); + if (slotIdxOpt == null) { + return null; + } + return discardSlotObject(slotIdxOpt); + } + + boolean destroyNow(long rawHandle) { + Integer slotIdxOpt = handleToSlotIdx.remove(rawHandle); + if (slotIdxOpt == null) { + return false; + } + destroySlotImpl(slotIdxOpt); + return true; + } + + void destroyDeferred(long rawHandle) { + Integer slotIdxOpt = handleToSlotIdx.remove(rawHandle); + if (slotIdxOpt == null) { + return; + } + int slotIdx = slotIdxOpt; + slots[slotIdx].markedForDestroy = true; + markObjectForUpdate(slotIdx); + } + + private void destroySlotImpl(int slotIdx) { + ObjectSlot slot = slots[slotIdx]; + setSlotObject(slotIdx, null); + updateObjectBlockPos(slotIdx, slot.getBlockPos(), null); + updateObjectAABB(slotIdx, slot.getChunkAABB(), null); + freeSlot(slotIdx); + } + + // SCENE LEVEL STATE + + @ApiBinding + public void runStateUpdates(@Nullable BlockSource region, int maxIterations) { + boolean anyUpdated; + do { + anyUpdated = false; + anyUpdated = updateAllMarkedChunks(region) || anyUpdated; + anyUpdated = updateAllMarkedObjects(region) || anyUpdated; + } while (anyUpdated && --maxIterations > 0); + } + + @ApiBinding + public void runDefaultTick(@Nullable BlockSource region, int maxTimeUs) { + runStateUpdates(region, 2); + runPeriodicChecks(8, 0.01f, maxTimeUs); + } + + @ApiBinding + public void setScopeLoaded(boolean loaded) { + if (this.isScopeLoaded == loaded) return; + isScopeLoaded = loaded; + for (int slotIdx = 0; slotIdx < slotsSize; slotIdx++) { + if (!loaded) setSlotLoaded(slotIdx, false); + else if (slots[slotIdx].posType == POS_TYPE_GLOBAL) { + setSlotLoaded(slotIdx, true); + } + } + } + + @ApiBinding + public boolean isScopeLoaded() { + return isScopeLoaded; + } + + @ApiBinding + public void clearAllObjects(boolean callDestroyForObjects) { + for (int slotIdx = 0; slotIdx < slotsSize; slotIdx++) { + if (callDestroyForObjects) { + setSlotObject(slotIdx, null); + } else { + discardSlotObject(slotIdx); + } + } + slots = new ObjectSlot[0]; + slotsSize = 0; + slotsFreeListHead = -1; + + periodicChecksList = new int[0]; + periodicChecksListSize = 0; + periodicChecksListNextIdx = 0; + + handleToSlotIdx.clear(); + chunks.clear(); + lookupByPos.clear(); + chunksMarkedForUpdate.clear(); + objectsMarkedForUpdate.clear(); + + unresolvedSavedObjectsList.clear(); + } + + @ApiBinding + public void markChunkForUpdate(int x, int z) { + for (int xi = x - 1; xi <= x + 1; xi++) + for (int zi = z - 1; zi <= z + 1; zi++) + markOneChunkForUpdate(new ChunkPos(xi, zi)); + } + + public void markChunkForUpdate(ChunkPos pos) { + markChunkForUpdate(pos.x, pos.z); + } + + // GETTERS + + boolean isAlive(long handle) { + return handleToSlotIdx.containsKey(handle); + } + + boolean isLoaded(long handle) { + Integer slotIdxOpt = handleToSlotIdx.get(handle); + return slotIdxOpt != null && slots[slotIdxOpt].isObjectLoaded; + } + + Object getObject(long handle) { + Integer slotIdxOpt = handleToSlotIdx.get(handle); + return slotIdxOpt == null ? null : slots[slotIdxOpt].object; + } + + BlockPos getBlockPos(long handle) { + Integer slotIdxOpt = handleToSlotIdx.get(handle); + return slotIdxOpt == null ? null : slots[slotIdxOpt].getBlockPos(); + } + + boolean setObject(long handle, Object object) { + Integer slotIdxOpt = handleToSlotIdx.get(handle); + if (slotIdxOpt == null) { + return false; + } + setSlotObject(slotIdxOpt, object); + markObjectForUpdate(slotIdxOpt); + return true; + } + + + // QUERY + + + private abstract class BaseQueryImpl implements GameObjectQuery { + @Override + public boolean isEmpty() { + return searchSlot(firstSlot()) == -1; + } + + @Override + public T one() { + int i = searchSlot(firstSlot()); + if (i == -1) return null; + return (T) slots[i].object; + } + + @Override + public GameObjectHandle oneHandle() { + int i = searchSlot(firstSlot()); + if (i == -1) return null; + return new GameObjectHandle(GameObjectManager.this, slots[i].handle); + } + + @Override + public Iterator iterator() { + return new Iterator() { + int i = firstSlot(); + @Override public boolean hasNext() { i = searchSlot(i); return i != -1; } + @Override public T next() { + if (i == -1) return null; + ObjectSlot slot = slots[i]; + i = getNextSlot(i); + return (T) slot.object; + } + }; + } + + @Override + public Iterator iterHandles() { + return new Iterator() { + int i = firstSlot(); + @Override public boolean hasNext() { i = searchSlot(i); return i != -1; } + @Override public GameObjectHandle next() { + if (i == -1) return null; + ObjectSlot slot = slots[i]; + i = getNextSlot(i); + return new GameObjectHandle(GameObjectManager.this, slot.handle); + } + }; + } + + @Override + public List toList() { + List list = new ArrayList<>(); + int i = searchSlot(firstSlot()); + while (i != -1) { + list.add((T) slots[i].object); + i = searchSlot(getNextSlot(i)); + } + return list; + } + + @Override + public List toHandleList() { + List list = new ArrayList<>(); + int i = searchSlot(firstSlot()); + while (i != -1) { + list.add(new GameObjectHandle(GameObjectManager.this, slots[i].handle)); + i = searchSlot(getNextSlot(i)); + } + return list; + } + + protected abstract int firstSlot(); + protected abstract int searchSlot(int idx); + protected abstract int getNextSlot(int idx); + } + + public GameObjectQuery queryAtBlock(BlockPos pos) { + return new BaseQueryImpl() { + @Override protected int firstSlot() { + Integer idx = lookupByPos.get(pos); + return idx != null ? idx : -1; + } + @Override protected int searchSlot(int idx) { return idx; } + @Override protected int getNextSlot(int idx) { return slots[idx].nextAtPos; } + }; + } + + public GameObjectQuery queryAtBlock(BlockPos pos, Class cls) { + return new BaseQueryImpl() { + @Override protected int firstSlot() { + Integer idx = lookupByPos.get(pos); + return idx != null ? idx : -1; + } + @Override protected int searchSlot(int i) { + ObjectSlot slot; + while (i != -1 && !cls.isInstance((slot = slots[i]).object)) { + i = slot.nextAtPos; + } + return i; + } + @Override protected int getNextSlot(int i) { return slots[i].nextAtPos; } + }; + } + + // TODO: optimize global object queries + + public GameObjectQuery queryGlobal() { + return new BaseQueryImpl() { + @Override protected int firstSlot() { return 0; } + @Override protected int searchSlot(int i) { + for (; i < slotsSize && (slots[i] == null || slots[i].posType != POS_TYPE_GLOBAL || slots[i].object == null); i++); + return i < slotsSize ? i : -1; + } + @Override protected int getNextSlot(int idx) { return idx + 1; } + }; + } + + public GameObjectQuery queryGlobal(Class cls) { + return new BaseQueryImpl() { + @Override protected int firstSlot() { return 0; } + @Override protected int searchSlot(int i) { + for (; i < slotsSize && (slots[i] == null || slots[i].posType != POS_TYPE_GLOBAL || cls.isInstance(slots[i].object)); i++); + return i < slotsSize ? i : -1; + } + @Override protected int getNextSlot(int idx) { return idx + 1; } + }; + } + + + // INTERNALS + + + private Chunk getOrCreateChunk(ChunkPos pos) { + Chunk chunk = chunks.get(pos); + if (chunk == null) { + chunk = new Chunk(); + chunk.pos = pos; + chunks.put(pos, chunk); + markOneChunkForUpdate(chunk); + } + return chunk; + } + + private int allocateSlot() { + if (slotsFreeListHead == -1) { + if (slotsSize >= slots.length) + slots = Arrays.copyOf(slots, Math.max(slots.length * 3 / 2 + 1, 32)); + int idx = slotsSize++; + slots[idx] = new ObjectSlot(); + return idx; + } + int idx = slotsFreeListHead; + slotsFreeListHead = slots[idx].nextAtPos; // in free list mode it is used as next in free list + return idx; + } + + private void freeSlot(int idx) { + ObjectSlot slot = slots[idx]; + setSlotObject(idx, null); + slot.nextAtPos = slotsFreeListHead; + slot.prevAtPos = -1; + slot.posType = POS_TYPE_NONE; + slot.handle = 0; + slot.markedForDestroy = slot.markedForUpdate = false; + slotsFreeListHead = idx; + } + + private static final int POS_TYPE_NONE = 0; // not valid + private static final int POS_TYPE_GLOBAL = 1; // always loaded if scene loaded + private static final int POS_TYPE_BLOCK = 2; // exists at block pos + + private static class ObjectSlot { + long handle = 0; + int x, y, z; + int posType = POS_TYPE_NONE; + Object object; + boolean markedForUpdate = false; + boolean markedForDestroy = false; + boolean isObjectInitialized = false; + boolean isObjectLoaded = false; + + int nextAtPos = -1; + int prevAtPos = -1; + + int periodicChecksListIdx = -1; + + BlockPos getBlockPos() { + if (posType == POS_TYPE_BLOCK) return new BlockPos(x, y, z); + return null; + } + + ChunkAABB getChunkAABB() { + if (posType == POS_TYPE_BLOCK) { + ChunkPos p = new ChunkPos(new BlockPos(x, y, z)); + return new ChunkAABB(p.x, p.z, p.x + 1, p.z + 1); + } + return null; + } + + boolean isSerializationNeeded(SerializationContext ctx) { + if (object == null) + return false; + return ObjectSerializer.getSerializerTypeIdFromObject(object) != null; + } + } + + private ObjectSlot[] slots = new ObjectSlot[0]; + int slotsSize = 0; + int slotsFreeListHead = -1; + + private void setSlotLoaded(int slotIdx, boolean isLoaded) { + ObjectSlot slot = slots[slotIdx]; + if (slot.isObjectLoaded != isLoaded) { + // ICLog.d("GameObjectManager", "setSlotLoaded idx=" + slotIdx + " o=" + slots[slotIdx].object + " loaded=" + isLoaded + " callInit=" + (isLoaded && !slot.isObjectInitialized) + " pos=" + slots[slotIdx].getBlockPos()); + Object o = slot.object; + if (isLoaded && !slot.isObjectInitialized) { + // make sure object is initialized before being loaded + ComponentUtils.safeCallInitFor(o); + slot.isObjectInitialized = true; + } + slot.isObjectLoaded = isLoaded; + ComponentUtils.safeCallLoadOrUnloadFor(o, isLoaded); + setPeriodicChecksEnables(slotIdx, slot, isLoaded && o instanceof PeriodicObjectCheckComponent); + } + } + + private void setSlotObject(int slotIdx, Object object) { + ObjectSlot slot = slots[slotIdx]; + if (slot.object != object) { + setSlotLoaded(slotIdx, false); + if (slot.isObjectInitialized) { + ComponentUtils.safeCallDestroyFor(slot.object); + } + ComponentUtils.safeCallFinalizeObjectFor(slot.object); + slot.object = object; + slot.isObjectInitialized = false; + } + } + + private Object discardSlotObject(int slotIdx) { + ObjectSlot slot = slots[slotIdx]; + Object o = null; + if (slot.object != null) { + setSlotLoaded(slotIdx, false); + if (slot.isObjectInitialized) { + ComponentUtils.safeCallDiscardFor(slot.object); + } + ComponentUtils.safeCallFinalizeObjectFor(slot.object); + o = slot.object; + slot.object = null; + slot.isObjectInitialized = false; + } + return o; + } + + public class Chunk { + ChunkPos pos; + // TODO: implement heap for logN add/remove + int[] objects; + int objectCnt = 0; + int loadState = STATE_NOT_LOADED; + boolean markedForUpdate = false; + + int getCurLoadedState(BlockSource region) { + int x = pos.x, z = pos.z; + if (region == null || !region.isChunkLoaded(x, z)) + return STATE_NOT_LOADED; + return region.isChunkLoaded(x - 1, z - 1) && + region.isChunkLoaded(x + 1, z + 1) && + region.isChunkLoaded(x - 1, z + 1) && + region.isChunkLoaded(x + 1, z - 1) ? STATE_LOADED : STATE_LAZY_LOADED; + } + + void add(int idx) { + if (objects == null || objectCnt == objects.length) { + if (objects == null) { + objects = new int[slots.length]; + } else { + objects = Arrays.copyOf(objects, objects.length * 3 / 2 + 1); + } + } + objects[objectCnt++] = idx; + } + + void remove(int idx) { + for (int i = 0; i < objectCnt; i++) { + if (objects[i] == idx) { + objects[i] = objects[--objectCnt]; + } + } + } + } + + private HashMap chunks = new HashMap<>(); + private HashMap handleToSlotIdx = new HashMap<>(); + private HashMap lookupByPos = new HashMap<>(); + + private void updateObjectBlockPos(int slotIdx, BlockPos prevPos, BlockPos newPos) { + if (prevPos != null && prevPos.equals(newPos)) { + return; + } + ObjectSlot slot = slots[slotIdx]; + if (prevPos != null) { + Integer slotIdxByPos = lookupByPos.get(prevPos); + if (slotIdxByPos != null && slotIdxByPos == slotIdx) lookupByPos.remove(prevPos); + if (slot.prevAtPos != -1) slots[slot.prevAtPos].nextAtPos = slot.nextAtPos; + if (slot.nextAtPos != -1) slots[slot.nextAtPos].prevAtPos = slot.prevAtPos; + slot.prevAtPos = slot.nextAtPos = -1; + } + if (newPos != null) { + Integer firstSlotIdx = lookupByPos.get(newPos); + if (firstSlotIdx != null) { + ObjectSlot first = slots[slotIdx]; + slot.nextAtPos = first.nextAtPos; + if (first.nextAtPos != -1) slots[first.nextAtPos].prevAtPos = slotIdx; + first.nextAtPos = slotIdx; + slot.prevAtPos = firstSlotIdx; + } else { + lookupByPos.put(newPos, slotIdx); + } + } + } + + private void updateObjectAABB(int slotIdx, ChunkAABB prevAABB, ChunkAABB newAABB) { + if (newAABB != null && newAABB.equals(prevAABB)) { + return; + } + if (prevAABB != null) + for (int x = prevAABB.x1, x2 = prevAABB.x2; x < x2; x++) { + for (int z = prevAABB.z1, z2 = prevAABB.z2; z < z2; z++) { + if (newAABB == null || !newAABB.contains(x, z)) { + Chunk chunk = chunks.get(new ChunkPos(x, z)); + if (chunk != null) { + chunk.remove(slotIdx); + if (chunk.objectCnt == 0) + chunks.remove(new ChunkPos(x, z)); + } + } + } + } + if (newAABB != null) + for (int x = newAABB.x1, x2 = newAABB.x2; x < x2; x++) { + for (int z = newAABB.z1, z2 = newAABB.z2; z < z2; z++) { + if (prevAABB == null || !prevAABB.contains(x, z)) { + getOrCreateChunk(new ChunkPos(x, z)).add(slotIdx); + } + } + } + } + + private void serializeOneObject(IScopedWriter scope, ObjectSlot slot, String typeId, SerializationContext ctx) { + IArrayWriter writer = scope.asArray(); + writer.addInt(slot.posType); + writer.addString(typeId); + writer.addLong(slot.handle); + ObjectSerializer.serializeObjectUnpacked(writer.beginScope(), slot.object, typeId, ctx, true); + switch (slot.posType) { + case POS_TYPE_GLOBAL: + break; + case POS_TYPE_BLOCK: + writer.addInt(slot.x).addInt(slot.y).addInt(slot.z); + break; + } + scope.endScope(); + } + + private GameObjectHandle deserializeAndInitOneObject(IScopedReader scope, SerializationContext ctx, IArrayWriter unresolvedList) { + IArrayReader reader = scope.asArray(); + if (reader == null || reader.size() < 4) { + ctx.addError("unexpected data format for game object"); + scope.endScope(); + return null; + } + int posType = reader.getInt(0, POS_TYPE_NONE); + String typeId = reader.getString(1, null); + long rawHandle = reader.getLong(2, -1); + if (posType == POS_TYPE_NONE || typeId == null || rawHandle < 0 || !reader.getValueType(3).isScope()) { + ctx.addError("unexpected data for game object posType=" + posType + " typeId=" + typeId + " rawHandle=" + rawHandle); + scope.endScope(); + return null; + } + if (!ObjectSerializer.isTypeRegistered(typeId)) { + // no saver registered for this type, keep serialized object to avoid loosing data + SerializationUtils.readerToWriter(scope, unresolvedList.beginScope()); + scope.endScope(); + return null; + } + Object object = ObjectSerializer.deserializeObjectUnpacked(reader.beginScope(3), typeId, Object.class, ctx, true); + if (object == null) { + // object deserialized into null - it doesn't want to be added to scene or failed + scope.endScope(); + return null; + } + GameObjectHandle handle = null; + switch (posType) { + case POS_TYPE_GLOBAL: + handle = create(object).placeAsGlobal(); + break; + case POS_TYPE_BLOCK: + if (reader.size() < 7) { + ctx.addError("failed to deserialize object block pos"); + return null; + } + int x = reader.getInt(4, Integer.MAX_VALUE); + int y = reader.getInt(5, Integer.MAX_VALUE); + int z = reader.getInt(6, Integer.MAX_VALUE); + if (x == Integer.MAX_VALUE || y == Integer.MAX_VALUE || z == Integer.MAX_VALUE) { + ctx.addError("failed to deserialize object block pos"); + return null; + } + handle = create(object).placeAtBlockPos(new BlockPos(x, y, z)); + break; + } + if (handle != null) { + ComponentUtils.safeCallCreateObjectFor(object, handle); + } + scope.endScope(); + return handle; + } + + private void deserializeAllObjects(IArrayReader objects, SerializationContext ctx, IArrayWriter unresolvedList) { + for (int i = 0; i < objects.size(); i++) { + deserializeAndInitOneObject(objects.beginScope(i), ctx, unresolvedList); + } + } + + private final List objectsMarkedForUpdate = new ArrayList<>(); + private final List chunksMarkedForUpdate = new ArrayList<>(); + + private void markOneChunkForUpdate(ChunkPos pos) { + Chunk chunk = chunks.get(pos); + if (chunk != null) + markOneChunkForUpdate(chunk); + } + + private void markOneChunkForUpdate(Chunk chunk) { + if (!chunk.markedForUpdate) { + chunksMarkedForUpdate.add(chunk); + chunk.markedForUpdate = true; + } + } + + private void markObjectForUpdate(int slotIdx) { + ObjectSlot slot = slots[slotIdx]; + if (!slot.markedForUpdate) { + objectsMarkedForUpdate.add(slotIdx); + slot.markedForUpdate = true; + } + } + + private boolean updateObjectLoadedStateInChunk(int slotIdx, Chunk baseChunk, ChunkAABB chunkAABB) { + boolean isObjectLoaded = true; + int baseX = baseChunk.pos.x, baseZ = baseChunk.pos.z; + for (int x = chunkAABB.x1, x2 = chunkAABB.x2; x < x2 && isObjectLoaded; x++) { + for (int z = chunkAABB.z1, z2 = chunkAABB.z2; z < z2 && isObjectLoaded; z++) { + Chunk c = x == baseX && z == baseZ ? baseChunk : chunks.get(new ChunkPos(x, z)); + isObjectLoaded = c.loadState == STATE_LOADED; // TODO: finer state logic support + } + } + setSlotLoaded(slotIdx, isObjectLoaded); + return isObjectLoaded; + } + + private boolean updateObjectLoadedState(int slotIdx, ObjectSlot slot) { + if (slot.posType == POS_TYPE_BLOCK) { + Chunk chunk = chunks.get(new ChunkPos(slot.x, slot.y, slot.z)); + if (chunk != null) { + return updateObjectLoadedStateInChunk(slotIdx, chunk, slot.getChunkAABB()); + } + // should not happen + return slot.isObjectLoaded; + } else if (slot.posType == POS_TYPE_GLOBAL) { + setSlotLoaded(slotIdx, isScopeLoaded); + } else if (slot.posType == POS_TYPE_NONE) { + setSlotLoaded(slotIdx, false); + } + return slot.isObjectLoaded; + } + + private boolean updateAllMarkedChunks(BlockSource region) { + if (chunksMarkedForUpdate.isEmpty()) + return false; + ArrayList chunksToUpdate = new ArrayList<>(); + for (Chunk chunk : chunksMarkedForUpdate) { + chunk.markedForUpdate = false; + int state = chunk.getCurLoadedState(region); + if (state != chunk.loadState) { + //ICLog.d("GameObjectManager", "chunk state changed pos=" + chunk.pos + " state=" + chunk.loadState + "->" + state); + chunk.loadState = state; + chunksToUpdate.add(chunk); + } + } + chunksMarkedForUpdate.clear(); + for (Chunk chunk : chunksToUpdate) { + for (int i = 0; i < chunk.objectCnt; i++) { + int slotIdx = chunk.objects[i]; + ObjectSlot slot = slots[slotIdx]; + slot.markedForUpdate = false; + if (slot.object == null) continue; + ChunkAABB chunkAABB = slot.getChunkAABB(); + if (chunkAABB == null) continue; + updateObjectLoadedStateInChunk(slotIdx, chunk, chunkAABB); + } + } + return !chunksToUpdate.isEmpty(); + } + + private boolean updateAllMarkedObjects(BlockSource region) { + if (objectsMarkedForUpdate.isEmpty()) + return false; + for (int slotIdx : objectsMarkedForUpdate) { + ObjectSlot slot = slots[slotIdx]; + if (slot.markedForDestroy) { + destroySlotImpl(slotIdx); + continue; + } + if (slot.markedForUpdate) { + updateObjectLoadedState(slotIdx, slot); + slot.markedForUpdate = false; + } + } + objectsMarkedForUpdate.clear(); + return true; + } + + + // PERIODIC CHECKS + + + private int[] periodicChecksList = new int[0]; + private int periodicChecksListSize = 0; + private int periodicChecksListNextIdx = 0; + + private void setPeriodicChecksEnables(int slotIdx, ObjectSlot slot, boolean isEnabled) { + if ((slot.periodicChecksListIdx != -1) == isEnabled) + return; + if (isEnabled) { + // insert + slot.periodicChecksListIdx = periodicChecksListSize++; + if (periodicChecksListSize >= periodicChecksList.length) { + periodicChecksList = Arrays.copyOf(periodicChecksList, Math.max(periodicChecksList.length * 3 / 2 + 1, periodicChecksListSize + 32)); + } + periodicChecksList[slot.periodicChecksListIdx] = slotIdx; + } else { + // unordered erase + int lastIdx = periodicChecksListSize - 1; + if (lastIdx != slot.periodicChecksListIdx) { + slots[periodicChecksList[lastIdx]].periodicChecksListIdx = slot.periodicChecksListIdx; + periodicChecksList[slot.periodicChecksListIdx] = periodicChecksList[lastIdx]; + } + slot.periodicChecksListIdx = -1; + periodicChecksListSize--; + } + } + + public void runPeriodicChecks(int maxChecksAbs, float maxChecksRel, int maxTimeUs) { + int numChecks = Math.min(maxChecksAbs, Math.max(1, (int) (periodicChecksListSize * maxChecksRel))); + if (numChecks <= 0 || periodicChecksListSize == 0) + return; + periodicChecksListNextIdx %= periodicChecksListSize; + for (int i = 0; i < numChecks; i++) { + int idx = periodicChecksListNextIdx; + int slotIdx = periodicChecksList[idx]; + ObjectSlot slot = slots[slotIdx]; + Object o = slot.object; + if (o instanceof PeriodicObjectCheckComponent) { + ComponentUtils.safeCallPeriodicCheckFor(o); + ++periodicChecksListNextIdx; + } else { + setPeriodicChecksEnables(slotIdx, slot, false); + } + if (periodicChecksListNextIdx >= periodicChecksListSize) + periodicChecksListNextIdx = 0; + } + } + + + // GLOBAL + + + // - uses server-side block source, not loaded and not saved on client + // - objects are saved + @ApiBinding + public static GameObjectManager getForDimension(int dimensionId) { + GameObjectManager scene = defaultMgrForDimension.get(dimensionId); + if (scene == null) { + defaultMgrForDimension.put(dimensionId, scene = new GameObjectManager("default-" + dimensionId).setDimensionId(dimensionId)); + } + return scene; + } + private static final HashMap defaultMgrForDimension = new HashMap<>(); + private static final GameObjectManager defaultServerMgr = new GameObjectManager("default-server-global"); + + @ApiBinding + public static GameObjectManager getGlobalForServer() { return defaultServerMgr; } + + // - on client uses client-side block source + // - cleared when going between dimensions + // - not loaded on server + @ApiBinding + public static GameObjectManager getClientSide() { return clientSideScene; } + private static final GameObjectManager clientSideScene = new GameObjectManager("default-client"); + + public static void clearAllDefaultServerScenes(boolean callDestroyForObjects) { + for (GameObjectManager mgr : defaultMgrForDimension.values()) { + mgr.clearAllObjects(callDestroyForObjects); + } + defaultMgrForDimension.clear(); + defaultServerMgr.clearAllObjects(callDestroyForObjects); + } + + public static void clearAllDefaultClientScenes(boolean callDestroyForObjects) { + clientSideScene.clearAllObjects(callDestroyForObjects); + } + + /* + @ApiBinding + public Scene getPersistent() { return globalPersistent; } + private final Scene globalPersistent = new Scene("default-persistent"); + */ + + public static HashMap getForAllExistingDimensions() { return defaultMgrForDimension; } + + public static void runTickForDefaultsOnClient() { + clientSideScene.runDefaultTick(BlockSource.getCurrentClientRegion(), 500); + } + + public static void runTickForDefaultsOnServer() { + defaultServerMgr.runDefaultTick(null, 500); + for (GameObjectManager mgr : defaultMgrForDimension.values()) { + mgr.runDefaultTick(BlockSource.getDefaultForDimension(mgr.getDimensionId()), 500); + } + } + + + // SAVES + + + private final OpaqueSerializedData unresolvedSavedObjectsList = new OpaqueSerializedData(); + + void deserializeAllObjectsAndKeepUnresolved(IScopedReader scope, SerializationContext ctx) { + if (scope.isArray()) { + unresolvedSavedObjectsList.clear(); + IArrayWriter unresolvedList = unresolvedSavedObjectsList.beginWrite().asArray(); + deserializeAllObjects(scope.asArray(), ctx, unresolvedList); + unresolvedList.endScope(); + } + scope.endScope(); + } + + void serializeAllObjectsAndAddUnresolved(IScopedWriter scope, SerializationContext ctx) { + IArrayWriter writer = scope.asArray(); + + if (!unresolvedSavedObjectsList.isEmpty()) { + IArrayReader unresolvedObjectsList = unresolvedSavedObjectsList.beginRead().asArray(); + for (int i = 0; i < unresolvedObjectsList.size(); i++) { + SerializationUtils.readerToWriter(unresolvedObjectsList.beginScope(i), writer.beginScope()); + } + unresolvedObjectsList.endScope(); + } + + for (int i = 0; i < slotsSize; i++) { + ObjectSlot slot = slots[i]; + if (slot.object == null || slot.posType == POS_TYPE_NONE) continue; + String typeId = ObjectSerializer.getSerializerTypeIdFromObject(slot.object); + if (typeId != null) + serializeOneObject(writer.beginScope(), slot, typeId, ctx); + } + scope.endScope(); + } + + public static void loadClass() {} + + static { + WorldDataScopeRegistry.getInstance().addScope("ic::game_objects", new WorldDataScopeRegistry.SaverScope() { + @Override + public void read(IScopedReader io, SerializationContext ctx) { + IObjectReader defaultScopes = io.asObject(); + if (defaultScopes == null) { + io.endScope(); + return; + } + for (String key : defaultScopes.iterKeys()) { + if (key.equals("global")) { + defaultServerMgr.deserializeAllObjectsAndKeepUnresolved(defaultScopes.beginScope(key), ctx); + } else if (key.startsWith("dim-")) { + int dimensionId; + try { + dimensionId = Integer.parseInt(key.substring(4)); + } catch (NumberFormatException e) { + ctx.addError("invalid key in default game object scopes " + key); + continue; + } + getForDimension(dimensionId).deserializeAllObjectsAndKeepUnresolved(defaultScopes.beginScope(key), ctx); + } + } + } + + @Override + public void write(IScopedWriter io, SerializationContext ctx) { + IObjectWriter defaultScopes = io.asObject(); + defaultServerMgr.serializeAllObjectsAndAddUnresolved(defaultScopes.beginScope("global"), ctx); + for (GameObjectManager mgr : defaultMgrForDimension.values()) { + mgr.serializeAllObjectsAndAddUnresolved(defaultScopes.beginScope("dim-" + mgr.dimensionId), ctx); + } + io.endScope(); + } + }); + } +} diff --git a/src/main/java/org/innercore/icstd/scene/GameObjectQuery.java b/src/main/java/org/innercore/icstd/scene/GameObjectQuery.java new file mode 100644 index 000000000..d6e983e25 --- /dev/null +++ b/src/main/java/org/innercore/icstd/scene/GameObjectQuery.java @@ -0,0 +1,15 @@ +package org.innercore.icstd.scene; + +import java.util.Iterator; +import java.util.List; + +public interface GameObjectQuery extends Iterable { + boolean isEmpty(); + + T one(); + List toList(); + + GameObjectHandle oneHandle(); + Iterator iterHandles(); + List toHandleList(); +} diff --git a/src/main/java/org/innercore/icstd/scene/LoadableComponent.java b/src/main/java/org/innercore/icstd/scene/LoadableComponent.java new file mode 100644 index 000000000..d61e8582c --- /dev/null +++ b/src/main/java/org/innercore/icstd/scene/LoadableComponent.java @@ -0,0 +1,8 @@ +package org.innercore.icstd.scene; + +import org.innercore.icstd.component.ComponentUtils; + +public interface LoadableComponent { + void load(); + void unload(); +} diff --git a/src/main/java/org/innercore/icstd/scene/PeriodicObjectCheckComponent.java b/src/main/java/org/innercore/icstd/scene/PeriodicObjectCheckComponent.java new file mode 100644 index 000000000..bc63767ff --- /dev/null +++ b/src/main/java/org/innercore/icstd/scene/PeriodicObjectCheckComponent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.scene; + +public interface PeriodicObjectCheckComponent { + void doPeriodicObjectCheck(); +} diff --git a/src/main/java/org/innercore/icstd/script/CompiledScriptWrap.java b/src/main/java/org/innercore/icstd/script/CompiledScriptWrap.java new file mode 100644 index 000000000..0c18b60ea --- /dev/null +++ b/src/main/java/org/innercore/icstd/script/CompiledScriptWrap.java @@ -0,0 +1,21 @@ +package org.innercore.icstd.script; + +import org.mozilla.javascript.Script; + +public class CompiledScriptWrap { + private final Script script; + private final ScriptObjectWrap scopeWrap; + + CompiledScriptWrap(Script script, ScriptObjectWrap scopeWrap) { + this.script = script; + this.scopeWrap = scopeWrap; + } + + public ScriptObjectWrap getScope() { + return scopeWrap; + } + + public Object run() { + return script.exec(ScriptEngineWrap.assureContextForCurrentThread(), scopeWrap.obj); + } +} diff --git a/src/main/java/org/innercore/icstd/script/ScriptEngineWrap.java b/src/main/java/org/innercore/icstd/script/ScriptEngineWrap.java new file mode 100644 index 000000000..d165dd404 --- /dev/null +++ b/src/main/java/org/innercore/icstd/script/ScriptEngineWrap.java @@ -0,0 +1,39 @@ +package org.innercore.icstd.script; + +import com.zhekasmirnov.innercore.api.mod.API; +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Scriptable; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +public class ScriptEngineWrap { + static Context assureContextForCurrentThread() { return Compiler.assureContextForCurrentThread(); } + public static Object getOrCreateOpaqueScriptContext() { return Compiler.assureContextForCurrentThread(); } + + public static Object evalInScope(String src, ScriptObjectWrap scope, String name) { + return assureContextForCurrentThread().evaluateString((Scriptable) scope.passToScript(), src, name, 0, null); + } + + public static CompiledScriptWrap compileScriptFromReader(Reader src, Object apiInst, String name) { + try { + Context ctx = assureContextForCurrentThread(); + ScriptObjectWrap scope = ScriptObjectWrap.create(ctx.initStandardObjects(apiInst != null ? ((API) apiInst).newInstance() : ScriptableObjectHelper.createEmpty(), false)); + return new CompiledScriptWrap(ctx.compileReader(src, name, 1, null), scope); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static CompiledScriptWrap compileScript(String src, API apiInst, String name) { + return compileScriptFromReader(new StringReader(src), apiInst, name); + } + + public static void initForThisThread() { + assureContextForCurrentThread(); + } + +} diff --git a/src/main/java/org/innercore/icstd/script/ScriptObjectComponent.java b/src/main/java/org/innercore/icstd/script/ScriptObjectComponent.java new file mode 100644 index 000000000..a0991e89a --- /dev/null +++ b/src/main/java/org/innercore/icstd/script/ScriptObjectComponent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.script; + +public interface ScriptObjectComponent { + Object getScriptObject(); +} diff --git a/src/main/java/org/innercore/icstd/script/ScriptObjectWrap.java b/src/main/java/org/innercore/icstd/script/ScriptObjectWrap.java new file mode 100644 index 000000000..6ea2ea4ba --- /dev/null +++ b/src/main/java/org/innercore/icstd/script/ScriptObjectWrap.java @@ -0,0 +1,712 @@ +package org.innercore.icstd.script; + +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import com.zhekasmirnov.innercore.api.mod.util.ScriptableFunctionImpl; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.mozilla.javascript.*; + +import java.util.*; + +public class ScriptObjectWrap { + protected final Scriptable obj; + + private ScriptObjectWrap(Scriptable obj) { + this.obj = obj; + } + + // --------- basics --------- + + public static Object UNDEFINED = Undefined.instance; + + public static boolean isUndefined(Object o) { + return Undefined.isUndefined(o); + } + + // use only for things received from script + public static ScriptObjectWrap create(Object o) { + while (o instanceof Wrapper) { + o = ((Wrapper) o).unwrap(); + } + if (o instanceof ScriptObjectWrap) { + return (ScriptObjectWrap) o; + } + if (o instanceof Scriptable) { + return new ScriptObjectWrap((Scriptable) o); + } + return null; + } + + public static ScriptObjectWrap createOrMakeEmptyObj(Object o) { + ScriptObjectWrap wrap = ScriptObjectWrap.create(o); + return wrap != null ? wrap : ScriptObjectWrap.createNewEmptyObject(); + } + + public static ScriptObjectWrap createOrThrow(Object o, String paramName) { + ScriptObjectWrap wrap = ScriptObjectWrap.create(o); + if (wrap == null) throw new IllegalArgumentException("expected script object for " + paramName + " got " + o); + return wrap; + } + + public interface ScriptFunctionImpl { + Object call(Object[] args); + } + public static ScriptObjectWrap createJavaFunction(String name, ScriptFunctionImpl impl) { + return new ScriptObjectWrap(new ScriptableFunctionImpl() { + @Override + public String getClassName() { + return name; + } + + @Override + public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { + return impl.call(args); + } + }); + } + public static ScriptObjectWrap createJavaFunction(ScriptFunctionImpl impl) { + return createJavaFunction("anonymous_java", impl); + } + + public static ScriptObjectWrap wrapJavaClass(ScriptObjectWrap scope, String name) { + try { + return new ScriptObjectWrap(new NativeJavaClass(scope.obj, Class.forName(name), false)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public static boolean isScriptObjClass(Class cls) { + return ScriptObjectWrap.class.isAssignableFrom(cls) || Scriptable.class.isAssignableFrom(cls); + } + + public static boolean isScriptObj(Object o) { + while (o instanceof Wrapper) { + o = ((Wrapper) o).unwrap(); + } + return o instanceof ScriptObjectWrap || o instanceof Scriptable; + } + + // convert any java object into representation, that can be sent to script + public static Object objectToScript(Object o) { + if (o instanceof ScriptObjectWrap) { + return ((ScriptObjectWrap) o).passToScript(); + } + if (!(o instanceof Scriptable)) { + return Context.javaToJS(o, ScriptableObjectHelper.getDefaultScope()); + } + return o; + } + + // rhino only + public static long scriptObjectToLong(Object o, long def) { + while (o instanceof Wrapper) { + o = ((Wrapper) o).unwrap(); + } + if (o instanceof Number) + return ((Number) o).longValue(); + return def; + } + + public static double scriptObjectToDouble(Object o, double def) { + while (o instanceof Wrapper) { + o = ((Wrapper) o).unwrap(); + } + if (o instanceof Long) + return def; + if (o instanceof Number) + return ((Number) o).doubleValue(); + try { + return Double.parseDouble(((CharSequence) o).toString()); + } catch (Exception e) { + return def; + } + } + + public static boolean scriptObjectToBool(Object o) { + return Context.toBoolean(o); + } + + public static T scriptToJava(Object o, Class cls) { + return (T) Context.jsToJava(o, cls); + } + + // convert non-wrapped script object into java counterpart + public static Object objectToJava(Object o) { + if(o instanceof NativeJavaClass) + return o; + + while (o instanceof Wrapper) { + o = ((Wrapper) o).unwrap(); + } + if (o instanceof Scriptable) { + return new ScriptObjectWrap((Scriptable) o); + } + if (isUndefined(o) || o == UniqueTag.NOT_FOUND) + return null; + return o; + } + + public static ScriptObjectWrap createNewEmptyObject() { + return new ScriptObjectWrap(ScriptableObjectHelper.createEmpty()); + } + + public static ScriptObjectWrap createNewEmptyArray() { + return new ScriptObjectWrap(ScriptableObjectHelper.createEmptyArray()); + } + + public static ScriptObjectWrap createNewIntArray(int[] data) { + Object[] objData = new Object[data.length]; + for (int i = 0; i < data.length; i++) + objData[i] = data[i]; + return new ScriptObjectWrap(ScriptableObjectHelper.createArray(objData)); + } + + public static ScriptObjectWrap createNewArray(Object[] data) { + return new ScriptObjectWrap(ScriptableObjectHelper.createArray(data)); + } + + public static ScriptObjectWrap createNewArray(List data) { + return new ScriptObjectWrap(ScriptableObjectHelper.createArray(data)); + } + + public static ScriptObjectWrap createFromMap(Map map) { + return new ScriptObjectWrap(ScriptableObjectHelper.createFromMap(map)); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ScriptObjectWrap wrap = (ScriptObjectWrap) o; + return Objects.equals(obj, wrap.obj); + } + + @Override + public int hashCode() { + return Objects.hash(obj); + } + + // intentionally typeless, do not cast the result + public Object passToScript() { + return obj; + } + + public boolean isFunction() { + return obj instanceof Function; + } + + public boolean isArray() { + return obj instanceof NativeArray; + } + + public int getArrayLength() { + if (obj instanceof NativeArray) { + return ((NativeArray) obj).size(); + } + return -1; + } + + public Iterable iterStringKeys() { + if (obj instanceof ScriptableObject && !(obj instanceof NativeArray)) { + final Scriptable o = obj; + return () -> { + final Object[] ids = o.getIds(); + return new Iterator() { + int idx = 0; + + private void search() { + while (idx < ids.length) { + if (ids[idx] instanceof String) + break; + idx++; + } + } + + @Override + public boolean hasNext() { + search(); + return idx < ids.length; + } + + @Override + public String next() { + search(); + return (String) ids[idx++]; + } + }; + }; + + } + return () -> new Iterator() { + @Override + public boolean hasNext() { + return false; + } + + @Override + public String next() { + return null; + } + }; + } + + // --------- getters --------- + + public boolean hasKey(String key) { return obj.has(key, obj); } + public boolean hasKey(int key) { return obj.has(key, obj); } + + // for java objects only, will return null for scriptable + public Object getJavaObj(String key) { return objToObj(internalGetAndUnwrap(key)); } + public Object getJavaObj(int key) { return objToObj(internalGetAndUnwrap(key)); } + public Object getJavaObj(String key, Object def) { + Object o = getJavaObj(key); + return o != null ? o : def; + } + public Object getJavaObj(int key, Object def) { + Object o = getJavaObj(key); + return o != null ? o : def; + } + // java objects or v8::BigNum, useful for native objects wrappers + public Object getJavaObjOrLong(String key) { + Object o = internalGetAndUnwrap(key); + if (o instanceof Long) + return o; + return objToObj(o); + } + + // SLOW, USE ONLY IF REALLY NEEDED + // automatically decide the type of value + public Object getAnyFromScript(String key) { return objectToJava(obj.get(key, obj)); } + public Object getAnyFromScript(int key) { return objectToJava(obj.get(key, obj)); } + public Object getAnyFromScript(String key, Object def) { + Object val = getAnyFromScript(key); + return val != null ? val : def; + } + public Object getAnyFromScript(int key, Object def) { + Object val = getAnyFromScript(key); + return val != null ? val : def; + } + // v8::Object + public ScriptObjectWrap getScriptObj(String key) { return create(internalGetAndUnwrap(key)); } + public ScriptObjectWrap getScriptObj(int key) { return create(internalGetAndUnwrap(key)); } + public ScriptObjectWrap getScriptObj(String key, ScriptObjectWrap def) { + ScriptObjectWrap val = getScriptObj(key); + return val != null ? val : def; + } + public ScriptObjectWrap getScriptObj(int key, ScriptObjectWrap def) { + ScriptObjectWrap val = getScriptObj(key); + return val != null ? val : def; + } + // v8::String + public String getString(String key, String def) { return objToString(obj.get(key, obj), def); } + public String getString(int key, String def) { return objToString(obj.get(key, obj), def); } + public String getString(String key) { return objToString(obj.get(key, obj), null); } + public String getString(int key) { return objToString(obj.get(key, obj), null); } + // v8::Number + public int getInt(String key, int def) { return objToInt(obj.get(key, obj), def); } + public int getInt(int key, int def) { return objToInt(obj.get(key, obj), def); } + // v8::Number + public double getDouble(String key, double def) { return objToDouble(obj.get(key, obj), def); } + public double getDouble(int key, double def) { return objToDouble(obj.get(key, obj), def); } + public float getFloat(String key, float def) { return (float) getDouble(key, def); } + public float getFloat(int key, float def) { return (float) getDouble(key, def); } + // v8::BigInt + public long getLong(String key, long def) { return objToLong(internalGetAndUnwrap(key), def); } + public long getLong(int key, long def) { return objToLong(internalGetAndUnwrap(key), def); } + // v8::BigInt and v8::Number + public Number getNumber(String key) { return objToNumber(obj.get(key, obj)); } + public Number getNumber(int key) { return objToNumber(obj.get(key, obj)); } + // v8::Boolean, consider: do we need strict bools or not? + public boolean getBoolean(String key, boolean def) { return objToBoolean(obj.get(key, obj), def); } + public boolean getBoolean(int key, boolean def) { return objToBoolean(obj.get(key, obj), def); } + // return -1 if no prop exists + public int getTriBool(String key) { return objToTriBool(obj.get(key, obj)); } + public int getTriBool(int key) { return objToTriBool(obj.get(key, obj)); } + + // --------- setters --------- + public void delete(String key) { obj.delete(key); } + public void delete(int key) { obj.delete(key); } + + public int arrayAllocPush() { + if (isArray()) { + NativeArray arr = (NativeArray) obj; + int pos = (int) arr.getLength(); + arr.put(pos, arr, Undefined.instance); + return pos; + } + return -1; + } + + public int allocArrayInsert(int idx) { + if (!isArray()) + return -1; + NativeArray arr = (NativeArray) obj; + int id = idx; + if (hasKey(id)) { + Object element = Undefined.instance; + while (true) { + if (hasKey(id)) { + Object temp = arr.get(id); + arr.put(id, arr, element); + element = temp; + id++; + } else { + arr.put(id, arr, element); + break; + } + } + } + return idx; + } + + // SLOW, USE ONLY IF REALLY NEEDED + // automatically decide the type of value + public void setAnyFromJava(String key, Object value) { obj.put(key, obj, objectToScript(value)); } + public void setAnyFromJava(int key, Object value) { obj.put(key, obj, objectToScript(value)); } + // for java objects only + public void setJavaObj(String key, Object value) { obj.put(key, obj, javaObjToScript(value)); } + public void setJavaObj(int key, Object value) { obj.put(key, obj, javaObjToScript(value)); } + // v8::Object + public void setScriptObj(String key, ScriptObjectWrap value) { obj.put(key, obj, value != null ? value.obj : null); } + public void setScriptObj(int key, ScriptObjectWrap value) { obj.put(key, obj, value != null ? value.obj : null); } + // v8::String + public void setString(String key, String value) { obj.put(key, obj, value); } + public void setString(int key, String value) { obj.put(key, obj, value); } + // v8::Number + public void setInt(String key, int value) { obj.put(key, obj, value); } + public void setInt(int key, int value) { obj.put(key, obj, value); } + // v8::Number + public void setDouble(String key, double value) { obj.put(key, obj, value); } + public void setDouble(int key, double value) { obj.put(key, obj, value); } + public void setFloat(String key, float value) { obj.put(key, obj, value); } + public void setFloat(int key, float value) { obj.put(key, obj, value); } + // v8::BigInt + public void setLong(String key, long value) { obj.put(key, obj, longToScript(value)); } + public void setLong(int key, long value) { obj.put(key, obj, longToScript(value)); } + // v8::Boolean + public void setBoolean(String key, boolean value) { obj.put(key, obj, value); } + public void setBoolean(int key, boolean value) { obj.put(key, obj, value); } + + public void copyFieldsFrom(ScriptObjectWrap other) { + for (Object key : other.obj.getIds()) { + if (key instanceof String) { + obj.put((String) key, obj, other.obj.get((String) key, other.obj)); + } + } + } + + // --------- function invoke --------- + public static final Object[] NO_FUNC_ARGS = new Object[0]; + public static final Object NOT_A_FUNC = new Object(); + + public void invokeAsVoidFunc(ScriptObjectWrap thisObj, Object[] args) { + invokeAsFunc(obj, thisObj, args); + } + public ScriptObjectWrap invokeAsJavaObjFunc(ScriptObjectWrap thisObj, Object[] args) { + return ScriptObjectWrap.create(invokeAsFunc(obj, thisObj, args)); + } + public ScriptObjectWrap invokeAsScriptObjFunc(ScriptObjectWrap thisObj,Object[] args) { + return ScriptObjectWrap.create(invokeAsFunc(obj, thisObj, args)); + } + public int invokeAsIntFunc(ScriptObjectWrap thisObj,Object[] args, int def) { + return objToInt(invokeAsFunc(obj, thisObj, args), def); + } + public double invokeAsDoubleFunc(ScriptObjectWrap thisObj,Object[] args, double def) { + return objToDouble(invokeAsFunc(obj, thisObj, args), def); + } + public String invokeAsStringFunc(ScriptObjectWrap thisObj, Object[] args, String def) { + return objToString(invokeAsFunc(obj, thisObj, args), def); + } + public boolean invokeAsBooleanFunc(ScriptObjectWrap thisObj,Object[] args, boolean def) { + return objToBoolean(invokeAsFunc(obj, thisObj, args), def); + } + + public void invokeVoidMemberFunc(String name, Object[] args) { + invokeMemberFunc(name, this, args); + } + public ScriptObjectWrap invokeScriptObjMemberFunc(String name, Object[] args) { + return ScriptObjectWrap.create(invokeMemberFunc(name, this, args)); + } + public Object invokeJavaObjMemberFunc(String name, Object[] args) { + return objToObj(invokeMemberFunc(name, this, args)); + } + public int invokeIntMemberFunc(String name, Object[] args, int def) { + return objToInt(invokeMemberFunc(name, this, args), def); + } + public double invokeDoubleMemberFunc(String name, Object[] args, double def) { + return objToDouble(invokeMemberFunc(name, this, args), def); + } + public String invokeStringMemberFunc(String name, Object[] args, String def) { + return objToString(invokeMemberFunc(name, this, args), def); + } + public boolean invokeBooleanMemberFunc(String name, Object[] args, boolean def) { + return objToBoolean(invokeMemberFunc(name, this, args), def); + } + + + // --------- json --------- + + public String toJsonString() { + return NativeJSON.stringify(ScriptEngineWrap.assureContextForCurrentThread(), ScriptableObjectHelper.getDefaultScope(), obj, null, null).toString(); + } + + public static ScriptObjectWrap parseJsonString(String str) { + return ScriptObjectWrap.create(NativeJSON.parse(ScriptEngineWrap.assureContextForCurrentThread(), ScriptableObjectHelper.getDefaultScope(), str, (context, scriptable, scriptable1, objects) -> objects[1])); + } + + /* + public static ScriptObjectWrap parseJson(JSONArray json) { + ArrayList arr = new ArrayList<>(); + for (int key = 0; key < json.length(); key++) { + Object value = json.opt(key); + if (value instanceof JSONObject) { + arr.add(parseJson((JSONObject) value)); + } else if (value instanceof JSONArray) { + arr.add(parseJson((JSONArray) value)); + } else { + arr.add(value); + } + } + return ScriptObjectWrap.createNewArray(arr); + } + + public static ScriptObjectWrap parseJson(JSONObject json) { + ScriptObjectWrap result = ScriptObjectWrap.createNewEmptyObject(); + JSONArray keys = json.names(); + if (keys != null) { + for (int i = 0; i < keys.length(); i++) { + String key = keys.optString(i); + if (key != null) { + Object value = json.opt(key); + if (value instanceof JSONObject) { + result.setScriptObj(key, parseJson((JSONObject) value)); + } else if (value instanceof JSONArray) { + result.setScriptObj(key, parseJson((JSONArray) value)); + } else { + result.setAnyFromJava(key, value); + } + } + } + } + return result; + } + + public static ScriptObjectWrap parseJsonString(String str) { + boolean isArray = false; + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') + continue; + isArray = ch == '['; + break; + } + + try { + if (isArray) + return parseJson(new JSONArray(str)); + return parseJson(new JSONObject(str)); + } catch (JSONException e) { + throw new RuntimeException(e); + } + }*/ + + public float[] getFloatArray(String key, int minLength, float defaultValue) { + defaultValue = getFloat(key, defaultValue); + ScriptObjectWrap array = getScriptObj(key); + if (array != null) { + int length = array.getArrayLength(); + float[] jArray = new float[(int) Math.max(length, minLength)]; + for (int i = 0; i < jArray.length; i++) { + if (i < length) + jArray[i] = array.getFloat(i, defaultValue); + else + jArray[i] = defaultValue; + } + return jArray; + } else { + float[] jArray = new float[minLength]; + for (int i = 0; i < minLength; i++) + jArray[i] = defaultValue; + return jArray; + } + } + + + // --------- helper section, implementation - independent --------- + + + public float[] getColorTemplate(String key, float defaultValue) { + float[] color = getFloatArray(key, 4, defaultValue); + ScriptObjectWrap wrapper = getScriptObj(key); + if (wrapper != null) { + color[0] = wrapper.getFloat("r", color[0]); + color[1] = wrapper.getFloat("g", color[1]); + color[2] = wrapper.getFloat("b", color[2]); + color[3] = wrapper.getFloat("a", color[3]); + } + return color; + } + + public float[] getMinMaxTemplate(String key, float defaultValue) { + float[] arr = getFloatArray(key, 2, defaultValue); + ScriptObjectWrap wrapper = getScriptObj(key); + if (wrapper != null) { + arr[0] = wrapper.getFloat("min", arr[0]); + arr[1] = wrapper.getFloat("max", arr[1]); + } + if (arr[1] < arr[0]) { + arr[1] = arr[0]; + } + return arr; + } + + public float[] getVec3Template(String key, float defaultValue) { + float[] arr = getFloatArray(key, 3, defaultValue); + ScriptObjectWrap wrapper = getScriptObj(key); + if (wrapper != null) { + arr[0] = wrapper.getFloat("x", arr[0]); + arr[1] = wrapper.getFloat("y", arr[1]); + arr[2] = wrapper.getFloat("z", arr[2]); + } + return arr; + } + + public float[] getUVTemplate(String key) { + float[] arr = getFloatArray(key, 4, 0); + ScriptObjectWrap wrapper = getScriptObj(key); + if (wrapper != null) { + arr[0] = wrapper.getFloat("u1", arr[0]); + arr[1] = wrapper.getFloat("v1", arr[1]); + arr[2] = wrapper.getFloat("u2", arr[2]); + arr[3] = wrapper.getFloat("v2", arr[3]); + } + if (arr[0] == 0 && arr[1] == 0 && arr[2] == 0 && arr[3] == 0) { + arr[2] = 1; + arr[3] = 1; + } + return arr; + } + + // --------- rhino internals --------- + + + protected Object internalGetAndUnwrap(String key) { + Object o = obj.get(key, obj); + while (o instanceof Wrapper) + o = ((Wrapper) o).unwrap(); + return o; + } + + protected Object internalGetAndUnwrap(int key) { + Object o = obj.get(key, obj); + while (o instanceof Wrapper) + o = ((Wrapper) o).unwrap(); + return o; + } + + protected static Object invokeAsFunc(Object o, ScriptObjectWrap thisObj, Object[] args) { + if (o instanceof Function) { + Function f = (Function) o; + Scriptable scope = f.getParentScope(); + Object res = f.call(ScriptEngineWrap.assureContextForCurrentThread(), scope, thisObj != null ? thisObj.obj : scope, args); + return objectToJava(res); + } + return NOT_A_FUNC; + } + + protected Object invokeMemberFunc(String name, ScriptObjectWrap thisObj, Object[] args) { + Object o = obj.get(name, obj); + return invokeAsFunc(o, thisObj, args); + } + + protected static Object objToObj(Object o) { + o = objectToJava(o); + if (o instanceof Number || o instanceof Boolean || o instanceof Scriptable || o instanceof String) { + return o; + } + // todo: debug check, remove + if (o instanceof ScriptObjectWrap) { + throw new RuntimeException("script object wrap detected (1)"); + } + if (o == UniqueTag.NOT_FOUND) + return null; + return o; + } + + protected static String objToString(Object o, String def) { + o = objectToJava(o); + if (o instanceof CharSequence) { + return o.toString(); + } + return def; + } + + protected static int objToInt(Object o, int def) { + o = objectToJava(o); + if (o instanceof Number) { + return ((Number) o).intValue(); + } + return def; + } + + protected static double objToDouble(Object o, double def) { + o = objectToJava(o); + if (o instanceof Number) { + return ((Number) o).doubleValue(); + } + return def; + } + + protected static long objToLong(Object o, long def) { + o = objectToJava(o); + if (o instanceof Long) { + return (Long) o; + } + return def; + } + + protected static Number objToNumber(Object o) { + o = objectToJava(o); + while (o instanceof Wrapper) + o = ((Wrapper) o).unwrap(); + return o instanceof Number ? (Number) o : null; + } + + protected static boolean objToBoolean(Object o, boolean def) { + o = objectToJava(o); + if (o instanceof Boolean) { + return (Boolean) o; + } + return def; + } + + protected static int objToTriBool(Object o) { + o = objectToJava(o); + if (o instanceof Boolean) { + return (Boolean) o ? 1 : 0; + } + return -1; + } + + protected static Object longToScript(long value) { + return (Long) value; + } + + protected static Object javaObjToScript(Object value) { + if (value == null) { + return null; + } + if (value instanceof Scriptable) { + throw new RuntimeException("scriptable detected"); + } + if (value instanceof ScriptObjectWrap) { + throw new RuntimeException("script object wrap detected (2)"); + } + if (value instanceof Number || value instanceof Boolean || value instanceof String) { + throw new RuntimeException("passed number, boolean or string to javaObjToScript"); + } + return Context.javaToJS(value, ScriptableObjectHelper.getDefaultScope(), ScriptEngineWrap.assureContextForCurrentThread()); + } +} diff --git a/src/main/java/org/innercore/icstd/serialization/BaseIO.java b/src/main/java/org/innercore/icstd/serialization/BaseIO.java new file mode 100644 index 000000000..05257e0a5 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/BaseIO.java @@ -0,0 +1,255 @@ +package org.innercore.icstd.serialization; + +import org.json.JSONArray; +import org.json.JSONObject; + +import java.math.BigDecimal; + +public class BaseIO { + + public static ESerializedType getSerializedTypeFromObject(Object value) { + if (value instanceof Number) { + if (value instanceof Integer) { + return ESerializedType.INT; + } else if (value instanceof Double || value instanceof BigDecimal) { + return ESerializedType.DOUBLE; + } else if (value instanceof Long) { + return ESerializedType.LONG; + } else if (value instanceof Float) { + return ESerializedType.FLOAT; + } else if (value instanceof Short || value instanceof Byte) { + return ESerializedType.INT; + } + } else if (value instanceof String) { + return ESerializedType.STRING; + } else if (value instanceof Boolean) { + return ESerializedType.BOOLEAN; + } else if (value instanceof JSONObject) { + return ESerializedType.OBJECT_SCOPE; + } else if (value instanceof JSONArray) { + return ESerializedType.ARRAY_SCOPE; + } else if (value == null || value == JSONObject.NULL) { + return ESerializedType.EMPTY_SCOPE; + } + throw new IllegalArgumentException("unknown serialized value type: " + value.getClass() + " " + value); + } + + public static abstract class ScopedWriter implements IScopedWriter { + private final BaseWriter parent; + protected Object resultObj; + private BaseWriter cur; + private boolean inScope = true; + + public ScopedWriter() { parent = null;} + protected ScopedWriter(BaseWriter parent) { this.parent = parent; } + + @Override + public IObjectWriter asObject() { + if (!inScope) throw new IllegalStateException("not in scope"); + if (cur == null) { + cur = newObjectWriter(this); + } + if (cur instanceof IObjectWriter) { + return (IObjectWriter) cur; + } + throw new IllegalStateException("scoped writer was already used as non-object"); + } + + protected abstract BaseWriter newObjectWriter(ScopedWriter parent); + protected abstract BaseWriter newArrayWriter(ScopedWriter parent); + protected abstract ScopedWriter newScopedWriter(BaseWriter parent); + + @Override + public IArrayWriter asArray() { + if (!inScope) throw new IllegalStateException("not in scope"); + if (cur == null) { + cur = newArrayWriter(this); + } + if (cur instanceof IArrayWriter) { + return (IArrayWriter) cur; + } + throw new IllegalStateException("scoped writer was already used as non-array"); + } + + @Override + public void endScope() { + if (!inScope) throw new IllegalStateException("not in scope"); + if (parent != null) { + if (parent.scope == null) throw new IllegalStateException("internal error: scope is closed"); + if (parent.childScope == null) throw new IllegalStateException("internal error: not in child scope"); + parent.writeEndedChildScope(cur != null ? cur.endThisScope() : null); + parent.childScope = null; + } else { + resultObj = cur != null ? cur.endThisScope() : null; + } + inScope = false; + } + + @Override + public boolean isInScope() { + return inScope; + } + + @Override + public void forceEndScope(SerializationContext ctx) { + if (!inScope) return; + if (cur == null) { + endScope(); + return; + } + cur.forceEndScope(ctx); + inScope = false; + } + } + + public static abstract class BaseWriter { + protected ScopedWriter scope; + protected ScopedWriter childScope; + + protected BaseWriter(ScopedWriter scope) { this.scope = scope; } + + protected abstract void writeEndedChildScope(Object childData); + protected abstract Object endThisScope(); + + public void endScope() { + if (scope == null) throw new IllegalStateException("scope is already closed"); + scope.endScope(); + scope = null; + } + + protected void validateStateForWrite() { + if (childScope != null) throw new IllegalStateException("in child scope"); + else if (scope == null) throw new IllegalStateException("scope is closed"); + } + + protected ScopedWriter beginScopeImpl() { + validateStateForWrite(); + return (childScope = scope.newScopedWriter(this)); + } + + protected void forceEndScope(SerializationContext ctx) { + if (scope == null) return; + if (childScope != null) { + childScope.forceEndScope(ctx); + childScope = null; + } + endScope(); + } + } + + + + public static abstract class ScopedReader implements IScopedReader { + protected final Object data; + private BaseReader parent; + private BaseReader cur; + private boolean inScope = true; + + protected ScopedReader(BaseReader parent, Object data) { + this(data); + this.parent = parent; + } + + protected ScopedReader(Object data) { + this.data = data; + } + + protected void validateInScope() { + if (!inScope) throw new IllegalStateException("not in scope"); + } + + @Override + public IObjectReader asObject() { + validateInScope(); + if (isObject()) { + if (cur == null) cur = newObjectReader(this, data); + return (IObjectReader) cur; + } + return null; + } + + @Override + public IArrayReader asArray() { + validateInScope(); + if (isArray()) { + if (cur == null) cur = newArrayReader(this, data); + return (IArrayReader) cur; + } + return null; + } + + @Override + public void endScope() { + if (!inScope) throw new IllegalStateException("not in scope"); + inScope = false; + if (parent != null) { + if (parent.scope == null) throw new IllegalStateException("internal error: scope is closed"); + if (parent.childScope == null) throw new IllegalStateException("internal error: not in child scope"); + parent.endReadingChildScope(); + parent.childScope = null; + } + inScope = false; + } + + @Override + public boolean isInScope() { + return inScope; + } + + @Override + public void forceEndScope(SerializationContext ctx) { + if (!inScope) return; + if (cur == null) { + endScope(); + return; + } + cur.forceEndScope(ctx); + inScope = false; + } + + protected abstract BaseReader newObjectReader(ScopedReader parent, Object data); + protected abstract BaseReader newArrayReader(ScopedReader parent, Object data); + protected abstract ScopedReader newScopedReader(BaseReader parent, Object data); + } + + public static abstract class BaseReader { + protected ScopedReader scope; + protected ScopedReader childScope; + + protected BaseReader(ScopedReader scope) { this.scope = scope; } + + protected void validateStateForRead() { + if (childScope != null) throw new IllegalStateException("in child scope"); + else if (scope == null) throw new IllegalStateException("scope is closed"); + } + + protected void endReadingChildScope() {} + + public void endScope() { + if (scope == null) throw new IllegalStateException("scope is already closed"); + scope.endScope(); + scope = null; + } + + protected ScopedReader beginScopeImpl(Object data) { + validateStateForRead(); + return (childScope = scope.newScopedReader(this, data)); + } + + protected void forceEndScope(SerializationContext ctx) { + if (scope == null) return; + if (childScope != null) { + childScope.forceEndScope(ctx); + childScope = null; + } + endScope(); + } + + public void endChildScope() { + if (childScope == null) throw new IllegalStateException("in not in child scope"); + else if (scope == null) throw new IllegalStateException("scope is closed"); + childScope.endScope(); + } + } + +} diff --git a/src/main/java/org/innercore/icstd/serialization/ESerializedType.java b/src/main/java/org/innercore/icstd/serialization/ESerializedType.java new file mode 100644 index 000000000..966712191 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/ESerializedType.java @@ -0,0 +1,45 @@ +package org.innercore.icstd.serialization; + +public enum ESerializedType { + INVALID(false, false), + INT(true, false), + BYTE(true, false), + SHORT(true, false), + LONG(true, false), + FLOAT(true, false), + DOUBLE(true, false), + STRING(false, false), + BOOLEAN(false, false), + + EMPTY_SCOPE(false, true), + OBJECT_SCOPE(false, true), + ARRAY_SCOPE(false, true), + ; + + private final boolean isNumber; + private final boolean isScope; + + ESerializedType(boolean isNumber, boolean isScope) { + this.isNumber = isNumber; + this.isScope = isScope; + } + + public boolean isInvalid() { return this == INVALID; } + public boolean isNumber() { return isNumber; } + public boolean isString() { return this == STRING; } + public boolean isInt() { return this == INT; } + public boolean isLong() { return this == LONG; } + public boolean isFloat() { return this == FLOAT; } + public boolean isDouble() { return this == DOUBLE; } + public boolean isShort() { return this == SHORT; } + public boolean isByte() { return this == BYTE; } + public boolean isIntegerNumber() { return this == INT || this == LONG || this == BYTE || this == SHORT; } + public boolean isFloatNumber() { return this == FLOAT || this == DOUBLE; } + public boolean isBoolean() { return this == BOOLEAN; } + + public boolean isScope() { return isScope; } + public boolean isEmptyScope() { return this == EMPTY_SCOPE; } + public boolean isObject() { return this == OBJECT_SCOPE; } + public boolean isArray() { return this == ARRAY_SCOPE; } + public boolean isNonEmptyScope() { return isScope && this != EMPTY_SCOPE; } +} diff --git a/src/main/java/org/innercore/icstd/serialization/IArrayReader.java b/src/main/java/org/innercore/icstd/serialization/IArrayReader.java new file mode 100644 index 000000000..b0c959cee --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/IArrayReader.java @@ -0,0 +1,22 @@ +package org.innercore.icstd.serialization; + +public interface IArrayReader { + int size(); + + ESerializedType getValueType(int idx); + int getInt(int idx, int fallback); + long getLong(int idx, long fallback); + short getShort(int idx, short fallback); + byte getByte(int idx, byte fallback); + float getFloat(int idx, float fallback); + double getDouble(int idx, double fallback); + boolean getBoolean(int idx, boolean fallback); + String getString(int idx, String fallback); + + boolean isGetAnySupported(); + Object getAny(int idx, Object fallback); + + IScopedReader beginScope(int idx); + void endChildScope(); + void endScope(); +} diff --git a/src/main/java/org/innercore/icstd/serialization/IArrayWriter.java b/src/main/java/org/innercore/icstd/serialization/IArrayWriter.java new file mode 100644 index 000000000..1704c4085 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/IArrayWriter.java @@ -0,0 +1,39 @@ +package org.innercore.icstd.serialization; + +public interface IArrayWriter { + int size(); + + IArrayWriter addInt(int value); + IArrayWriter addInt(int idx, int value); + IArrayWriter setInt(int idx, int value); + IArrayWriter addLong(long value); + IArrayWriter addLong(int idx, long value); + IArrayWriter setLong(int idx, long value); + IArrayWriter addShort(short value); + IArrayWriter addShort(int idx, short value); + IArrayWriter setShort(int idx, short value); + IArrayWriter addByte(byte value); + IArrayWriter addByte(int idx, byte value); + IArrayWriter setByte(int idx, byte value); + IArrayWriter addFloat(float value); + IArrayWriter addFloat(int idx, float value); + IArrayWriter setFloat(int idx, float value); + IArrayWriter addDouble(double value); + IArrayWriter addDouble(int idx, double value); + IArrayWriter setDouble(int idx, double value); + IArrayWriter addBoolean(boolean value); + IArrayWriter addBoolean(int idx, boolean value); + IArrayWriter setBoolean(int idx, boolean value); + IArrayWriter addString(String value); + IArrayWriter addString(int idx, String value); + IArrayWriter setString(int idx, String value); + + boolean isPutAnySupported(); + void addAny(Object value); + void addAny(int idx, Object value); + void setAny(int idx, Object value); + + IScopedWriter beginScope(); + IScopedWriter beginScope(int idx); + void endScope(); +} diff --git a/src/main/java/org/innercore/icstd/serialization/IObjectReader.java b/src/main/java/org/innercore/icstd/serialization/IObjectReader.java new file mode 100644 index 000000000..2478db6c6 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/IObjectReader.java @@ -0,0 +1,24 @@ +package org.innercore.icstd.serialization; + +import java.util.Iterator; + +public interface IObjectReader { + boolean hasKey(String key); + ESerializedType getValueType(String key); + int getInt(String key, int fallback); + long getLong(String key, long fallback); + short getShort(String key, short fallback); + byte getByte(String key, byte fallback); + float getFloat(String key, float fallback); + double getDouble(String key, double fallback); + boolean getBoolean(String key, boolean fallback); + String getString(String key, String fallback); + + boolean isGetAnySupported(); + Object getAny(String key, Object fallback); + Iterable iterKeys(); + + IScopedReader beginScope(String key); + void endChildScope(); + void endScope(); +} diff --git a/src/main/java/org/innercore/icstd/serialization/IObjectSerializer.java b/src/main/java/org/innercore/icstd/serialization/IObjectSerializer.java new file mode 100644 index 000000000..24b4cc185 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/IObjectSerializer.java @@ -0,0 +1,6 @@ +package org.innercore.icstd.serialization; + +public interface IObjectSerializer { + T read(IScopedReader reader, SerializationContext ctx, String typeId); + void write(IScopedWriter writer, T object, SerializationContext ctx, String typeId); +} diff --git a/src/main/java/org/innercore/icstd/serialization/IObjectWriter.java b/src/main/java/org/innercore/icstd/serialization/IObjectWriter.java new file mode 100644 index 000000000..bfdaed1e5 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/IObjectWriter.java @@ -0,0 +1,18 @@ +package org.innercore.icstd.serialization; + +public interface IObjectWriter { + IObjectWriter putInt(String key, int value); + IObjectWriter putLong(String key, long value); + IObjectWriter putShort(String key, short value); + IObjectWriter putByte(String key, byte value); + IObjectWriter putFloat(String key, float value); + IObjectWriter putDouble(String key, double value); + IObjectWriter putBoolean(String key, boolean value); + IObjectWriter putString(String key, String value); + + boolean isPutAnySupported(); + void putAny(String key, Object value); + + IScopedWriter beginScope(String key); + void endScope(); +} diff --git a/src/main/java/org/innercore/icstd/serialization/IScopedReader.java b/src/main/java/org/innercore/icstd/serialization/IScopedReader.java new file mode 100644 index 000000000..b37c4822a --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/IScopedReader.java @@ -0,0 +1,14 @@ +package org.innercore.icstd.serialization; + +public interface IScopedReader { + boolean isEmpty(); + boolean isObject(); + boolean isArray(); + + IObjectReader asObject(); + IArrayReader asArray(); + + void endScope(); + boolean isInScope(); + void forceEndScope(SerializationContext ctx); +} diff --git a/src/main/java/org/innercore/icstd/serialization/IScopedWriter.java b/src/main/java/org/innercore/icstd/serialization/IScopedWriter.java new file mode 100644 index 000000000..8c29d052b --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/IScopedWriter.java @@ -0,0 +1,10 @@ +package org.innercore.icstd.serialization; + +public interface IScopedWriter { + IObjectWriter asObject(); + IArrayWriter asArray(); + + void endScope(); + boolean isInScope(); + void forceEndScope(SerializationContext ctx); +} diff --git a/src/main/java/org/innercore/icstd/serialization/ISerializableObject.java b/src/main/java/org/innercore/icstd/serialization/ISerializableObject.java new file mode 100644 index 000000000..9bb81cecd --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/ISerializableObject.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.serialization; + +public interface ISerializableObject { + String getSerializationTypeId(); +} diff --git a/src/main/java/org/innercore/icstd/serialization/ISerializerTag.java b/src/main/java/org/innercore/icstd/serialization/ISerializerTag.java new file mode 100644 index 000000000..1a5af794f --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/ISerializerTag.java @@ -0,0 +1,4 @@ +package org.innercore.icstd.serialization; + +public interface ISerializerTag { +} diff --git a/src/main/java/org/innercore/icstd/serialization/ObjectSerializer.java b/src/main/java/org/innercore/icstd/serialization/ObjectSerializer.java new file mode 100644 index 000000000..55dade363 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/ObjectSerializer.java @@ -0,0 +1,372 @@ +package org.innercore.icstd.serialization; + +import com.zhekasmirnov.innercore.api.log.ICLog; +import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; +import org.json.JSONObject; +import org.mozilla.javascript.*; + +import java.util.*; + +public class ObjectSerializer { + private static final HashMap serializerMap = new HashMap<>(); + private static final HashMap legacyHashIdLookup = new HashMap<>(); + + public static final String SKIP_SERIALIZATION_TYPE_ID = registerType("__serialization_skip", Object.class, new IObjectSerializer() { + @Override public Object read(IScopedReader reader, SerializationContext ctx, String typeId) { return null; } + @Override public void write(IScopedWriter writer, Object object, SerializationContext ctx, String typeId) { writer.endScope(); } + }, false); + + public static class SerializerRecord { + private IObjectSerializer serializer; + private Class objectType; + private String typeId; + private int legacyHashId; + + public Class getObjectType() { + return objectType; + } + + public IObjectSerializer getSerializer() { + return serializer; + } + + public String getTypeId() { + return typeId; + } + + public int getLegacyHashId() { + return legacyHashId; + } + } + + public static SerializerRecord getSerializerRecordByTypeId(String typeId) { + return serializerMap.get(typeId); + } + + public static SerializerRecord getSerializerRecordByLegacyHashId(int legacyHashId) { + return legacyHashIdLookup.get(legacyHashId); + } + + public static boolean isTypeRegistered(String saverTypeId) { + return getSerializerRecordByTypeId(saverTypeId) != null; + } + + public static IObjectSerializer getSerializerByTypeId(String typeId) { + SerializerRecord rec = getSerializerRecordByTypeId(typeId); + return rec != null ? rec.serializer : null; + } + + public static IObjectSerializer getSerializerByTypeId(String typeId, Class objectType) { + SerializerRecord rec = getSerializerRecordByTypeId(typeId); + return rec != null && objectType.isAssignableFrom(rec.objectType) ? (IObjectSerializer) rec.serializer : null; + } + + public static String getTypeIdFromLegacyHashId(int legacyHashId, String fallback) { + SerializerRecord rec = getSerializerRecordByLegacyHashId(legacyHashId); + return rec != null ? rec.typeId : fallback; + } + + public static String getTypeIdFromLegacyHashIdNotNull(int legacyHashId) { + String typeId = getTypeIdFromLegacyHashId(legacyHashId, null); + return typeId != null ? typeId : "__unknown_hash_id:" + legacyHashId; + } + + public static int getLegacyHashIdFromTypeId(String typeId, int fallback) { + SerializerRecord rec = getSerializerRecordByTypeId(typeId); + return rec != null ? rec.legacyHashId : fallback; + } + + + public static String registerType(String typeId, Class cls, IObjectSerializer saver, boolean canFail) { + int legacyHashId = typeId.hashCode(); + if (getSerializerRecordByTypeId(typeId) != null) { + if (!canFail) { + throw new IllegalArgumentException("Saver " + typeId + " was already occupied and cannot be registered twice!"); + } + return null; + } + + while (legacyHashIdLookup.containsKey(legacyHashId)) { + SerializerRecord rec = getSerializerRecordByLegacyHashId(legacyHashId); + ICLog.i("SAVER", "object saver legacy hash id collision (this will only affect loading from legacy saves) '" + typeId + "' and '" + rec.typeId + "' id=" + legacyHashId); + if (legacyHashId == 0) legacyHashId = 1; + legacyHashId ^= (legacyHashId << 13); + legacyHashId ^= (legacyHashId >>> 17); + legacyHashId ^= (legacyHashId << 5); + } + + SerializerRecord rec = new SerializerRecord(); + rec.serializer = saver; + rec.objectType = cls; + rec.legacyHashId = legacyHashId; + rec.typeId = typeId; + serializerMap.put(typeId, rec); + legacyHashIdLookup.put(legacyHashId, rec); + return typeId; // allow assigning it to variable + } + + public static String registerType(String typeId, Class cls, IObjectSerializer saver) { + return registerType(typeId, cls, saver, false); + } + + + + // NOTE: Null is not a primitive type, instead it is represented by creating an empty scope (writer.beginScope(...).endScope()) + public static boolean isPrimitiveType(Object o) { + return o instanceof Number || o instanceof Boolean || o instanceof CharSequence; + } + + public static class PackedObjectReader { + private final IScopedReader originalReader; + public final IScopedReader reader; + public final String typeId; + + private PackedObjectReader(IScopedReader originalReader, IScopedReader reader, String typeId) { + this.originalReader = originalReader; + this.reader = reader; + this.typeId = typeId; + } + + // must be called after reader.endScope() + public void endScope() { + if (reader != originalReader) { + originalReader.endScope(); + } + } + } + + // NOTE: returns null and leaves reader untouched, if it is not a packed object + public static PackedObjectReader unpackObjectAndTypeId(IScopedReader reader, SerializationContext ctx) { + IArrayReader array = reader.asArray(); + String typeId = null; + IScopedReader target = reader; + if (array != null && array.size() == 2 && array.getValueType(0).isString() && array.getValueType(1).isScope()) { + String str = array.getString(0, ""); + if (str.startsWith("_t:")) { + typeId = str.substring(3); + target = array.beginScope(1); + } + } + if (ctx.hasLegacyFormat) { + IObjectReader object = reader.asObject(); + if (object != null && object.getValueType(ObjectSaverRegistry.PROPERTY_SAVER_ID).isNumber()) { + int legacyTypeId = object.getInt(ObjectSaverRegistry.PROPERTY_SAVER_ID, 0); + typeId = getTypeIdFromLegacyHashIdNotNull(legacyTypeId); + } + } + if (typeId == null) { + return null; + } + return new PackedObjectReader(reader, target, typeId); + } + + // NOTE: in non-legacy format returns a new child writer for the payload, in legacy format returns the same writer. + // Each *distinct* writer must call end scope, starting from one returned, and then passed + public static IScopedWriter packObjectAndTypeId(IScopedWriter writer, String typeId, SerializationContext ctx) { + if (typeId == null) { + throw new IllegalArgumentException("typeId is null"); + } + if (ctx.forceLegacyFormat) { + IObjectWriter object = writer.asObject(); + SerializerRecord rec = getSerializerRecordByTypeId(typeId); + object.putAny(ObjectSaverRegistry.PROPERTY_SAVER_ID, rec != null ? rec.legacyHashId : -2); // -2 is a stub value + return writer; + } else { + IArrayWriter array = writer.asArray(); + array.addAny("_t:" + typeId); + return array.beginScope(); + } + } + + public static String getSerializerTypeIdFromObject(Object src) { + if (src instanceof ISerializableObject) { + String typeId = ((ISerializableObject) src).getSerializationTypeId(); + if (typeId != null) + return typeId; + } + if (src instanceof ObjectSaverRegistry.IHashSaver) { + int hashId = ((ObjectSaverRegistry.IHashSaver) src).getObjectSaverId(); + return getTypeIdFromLegacyHashIdNotNull(hashId); + } else if (src instanceof Scriptable) { + Scriptable scriptable = (Scriptable) src; + Object saverId = scriptable.get(ObjectSaverRegistry.PROPERTY_SAVER_ID, scriptable); + if (saverId != Scriptable.NOT_FOUND) { + if (saverId instanceof CharSequence) { + return saverId.toString(); + } else if (saverId instanceof Number) { + return getTypeIdFromLegacyHashIdNotNull(((Number) saverId).intValue()); + } + } + } else if (src instanceof JSONObject) { + Object saverId = ((JSONObject) src).opt(ObjectSaverRegistry.PROPERTY_SAVER_ID); + if (saverId instanceof String) { + return (String) saverId; + } else if (saverId instanceof Number) { + return getTypeIdFromLegacyHashIdNotNull(((Number) saverId).intValue()); + } + } else if (src instanceof Wrapper) { + return getSerializerTypeIdFromObject(((Wrapper) src).unwrap()); + } + return null; + } + + + public static boolean serializeObjectUnpacked(IScopedWriter io, Object src, String typeId, SerializationContext ctx, boolean optional) { + if (!io.isInScope()) + throw new IllegalStateException("writer scope is already closed"); + if (src == null) { + io.endScope(); + throw new IllegalArgumentException("serializeObjectUnpacked - object is null"); + } + SerializerRecord rec = getSerializerRecordByTypeId(typeId); + if (rec == null) { + String msg = "serializeObject: unknown saver type will be skipped " + typeId; + io.endScope(); + if (!optional) throw new IllegalArgumentException(msg); + else ctx.addError(msg); + return false; + } + if (!rec.objectType.isAssignableFrom(src.getClass())) { + String msg = "serializeObject: write() of serializer " + rec.serializer.getClass() + " expects " + rec.objectType + " but passed " + src.getClass(); + io.endScope(); + if (!optional) throw new IllegalArgumentException(msg); + else ctx.addError(msg); + return false; + } + boolean error = false; + try { + ((IObjectSerializer) rec.serializer).write(io, src, ctx, typeId); + } catch (Throwable err) { + error = true; + if (!optional) + throw err; + ctx.addError(err, src, "serializer.write() failed with error"); + } finally { + if (io.isInScope()) { + if (!error) ctx.addError(null, rec.serializer, "serializer didn't close its scope after exiting " + rec.serializer.getClass()); + io.forceEndScope(ctx); + } + } + return !error; + } + + public static boolean serializeObject(IScopedWriter io, Object o, SerializationContext ctx, boolean optional) { + if (!io.isInScope()) + throw new IllegalStateException("writer scope is already closed"); + if (o == null) { + io.endScope(); + if (optional) return true; + else throw new IllegalArgumentException("serializeObject(): non-nullable object is null (use serializeObjectOpt to write null)"); + } + String typeId = getSerializerTypeIdFromObject(o); + if (typeId == null) { + io.endScope(); + String msg = "object don't have associated saver type"; + if (optional) ctx.addError(null, o, msg); + else throw new IllegalArgumentException(msg); + return false; + } + IScopedWriter writer = packObjectAndTypeId(io, typeId, ctx); + boolean success; + try { + success = serializeObjectUnpacked(writer, o, typeId, ctx, optional); + } finally { + if (writer != io) + io.endScope(); + } + return success; + } + + // nullable, never throws (serializer errors are swallowed) + public static boolean serializeObjectOpt(IScopedWriter writer, Object o) { + return serializeObject(writer, o, new SerializationContext(), true); + } + + // non-null, throws on null or any serializer error + public static void serializeObjectStrict(IScopedWriter writer, Object o) { + serializeObject(writer, o, new SerializationContext(), false); + } + + public static T deserializeObjectUnpacked(IScopedReader io, String typeId, Class cls, SerializationContext ctx, boolean optional) { + if (!io.isInScope()) + throw new IllegalStateException("deserializeObjectUnpacked(): reader scope is already closed"); + SerializerRecord rec = getSerializerRecordByTypeId(typeId); + if (rec == null) { + io.endScope(); + String msg = "serializer not found for type " + typeId; + if (optional) ctx.addError(msg); + else throw new IllegalArgumentException("deserializeObjectUnpacked(): " + msg); + return null; + } + if (!cls.isAssignableFrom(rec.objectType)) { + io.endScope(); + String msg = "read() of serializer " + rec.serializer.getClass() + " returns " + rec.objectType + " but expected " + cls; + if (optional) ctx.addError(msg); + else throw new IllegalArgumentException("deserializeObjectUnpacked(): " + msg); + return null; + } + Object result = null; + boolean error = false; + try { + result = rec.serializer.read(io, ctx, typeId); + } catch (Throwable err) { + if (!optional) + throw err; + ctx.addError(err, null, "serializer.read() failed with error"); + error = true; + } + finally { + if (io.isInScope()) { + if (!error) ctx.addError(null, rec.serializer, "deserializer didn't close its scope after exiting " + rec.serializer.getClass()); + io.forceEndScope(ctx); + } + } + if (result == null) { + String msg = "serializer.read() returned null"; + if (!optional) throw new NullPointerException("deserializeObjectUnpacked(): " + msg); + } + if (result == null || cls.isInstance(result)) { + return (T) result; + } else { + String msg = "object deserialized into unexpected type " + result.getClass() + " (expected " + cls + ")"; + if (optional) ctx.addError(msg); + else throw new IllegalStateException("deserializeObjectUnpacked(): " + msg); + return null; + } + } + + public static T deserializeObject(IScopedReader io, Class cls, SerializationContext ctx, boolean optional) { + if (!io.isInScope()) + throw new IllegalStateException("deserializeObject(): reader scope is already closed"); + if (io.isEmpty()) { + io.endScope(); + if (optional) return null; + else throw new IllegalStateException("deserializeObject(): non-nullable object scope is empty"); + } + PackedObjectReader packed = unpackObjectAndTypeId(io, ctx); + if (packed == null) { + io.endScope(); + String msg = "not a packed serialized object"; + if (optional) ctx.addError(null, io, msg); + else throw new IllegalStateException("deserializeObject(): " + msg); + return null; + } + T result; + try { + result = deserializeObjectUnpacked(packed.reader, packed.typeId, cls, ctx, optional); + } finally { + packed.endScope(); + } + return result; + } + + // nullable, never throws + public static T deserializeObjectOpt(IScopedReader io, Class cls) { + return deserializeObject(io, cls, new SerializationContext(), true); + } + + // not-null, throws on any error or when null is read + public static T deserializeObjectStrict(IScopedReader io, Class cls) { + return deserializeObject(io, cls, new SerializationContext(), false); + } +} diff --git a/src/main/java/org/innercore/icstd/serialization/ObjectSerializerDebug.java b/src/main/java/org/innercore/icstd/serialization/ObjectSerializerDebug.java new file mode 100644 index 000000000..275d54721 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/ObjectSerializerDebug.java @@ -0,0 +1,143 @@ +package org.innercore.icstd.serialization; + +import com.zhekasmirnov.innercore.api.log.DialogHelper; +import com.zhekasmirnov.innercore.api.log.ICLog; +import org.innercore.internal.serialization.JsonIO; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +public class ObjectSerializerDebug { + + public static String serializedToDebugString(Object o, boolean allowCvt) { + Object cvt = o; + if (cvt == null) + cvt = o; + try { + if (cvt instanceof JSONObject) { + return ((JSONObject) cvt).toString(2); + } else if (cvt instanceof JSONArray) { + return ((JSONArray) cvt).toString(2); + } + } catch (JSONException e) { ICLog.e("SAVER", "JSON serialization failed", e); } + return "" + o; + } + + public static boolean isJsonEqual(Object o1, Object o2, boolean ignoreMissingNulls, List verbose) { + if (o1 == JSONObject.NULL) + o1 = null; + if (o2 == JSONObject.NULL) + o2 = null; + if (Objects.equals(o1, o2)) + return true; + if (o1 instanceof Number && o2 instanceof Number && !(o1 instanceof Long || o2 instanceof Long)) { + return ((Number) o1).doubleValue() == ((Number) o2).doubleValue(); + } + if (o1 instanceof JSONObject && o2 instanceof JSONObject) { + JSONObject j1 = (JSONObject) o1; + JSONObject j2 = (JSONObject) o2; + for (Iterator iter = j1.keys(); iter.hasNext(); ) { + String key = iter.next(); + if (!ignoreMissingNulls) { + if (!j2.has(key)) { + if (verbose != null) { + verbose.add("key mismatch: " + key); + } + return false; + } + } + else if (!isJsonEqual(j1.opt(key), j2.opt(key), ignoreMissingNulls, verbose)) + return false; + } + for (Iterator iter = j2.keys(); iter.hasNext(); ) { + String key = iter.next(); + if (!ignoreMissingNulls) { + if (!j1.has(key)) { + if (verbose != null) { + verbose.add("key mismatch: " + key); + } + return false; + } + } + else if (!isJsonEqual(j1.opt(key), j2.opt(key), ignoreMissingNulls, verbose)) + return false; + } + if (!ignoreMissingNulls) { + for (Iterator iter = j1.keys(); iter.hasNext(); ) { + String key = iter.next(); + if (!isJsonEqual(j1.opt(key), j2.opt(key), ignoreMissingNulls, verbose)) + return false; + } + } + return true; + } + if (o1 instanceof JSONArray && o2 instanceof JSONArray) { + JSONArray j1 = (JSONArray) o1; + JSONArray j2 = (JSONArray) o2; + if (j1.length() != j2.length()) { + if (verbose != null) { verbose.add("length mismatch: " + j1.length() + " != " + j2.length()); } + return false; + } + for (int i = 0; i < j1.length(); i++) { + if (!isJsonEqual(j1.opt(i), j2.opt(i), ignoreMissingNulls, verbose)) + return false; + } + return true; + } + if (verbose != null) { verbose.add("value mismatch: " + o1 + " != " + o2); } + return false; + } + + private static Object reSerialize(Object src, boolean forceLegacyFormat) { + try { + SerializationContext ctx = new SerializationContext(); + ctx.forceLegacyFormat = forceLegacyFormat; + JsonIO.ScopedReader reader = new JsonIO.ScopedReader(src); + Object deserialized = ScriptableObjectSerializer.readScriptable(reader, ctx); + JsonIO.ScopedWriter writer = new JsonIO.ScopedWriter(); + ScriptableObjectSerializer.writeScriptable(writer, deserialized, ctx); + return writer.getResult(); + } catch (Throwable t) { + ICLog.e("SAVER", "Serialization test failed", t); + return null; + } + } + + private static boolean validateEqual(String testName, String o1name, Object o1, String o2name, Object o2) { + List verbose = new ArrayList<>(); + boolean passed = isJsonEqual(o1, o2, true, verbose); + ICLog.d("SAVER", "SERIALIZATION " + testName + " PASSED: " + passed + " " + verbose); + if (!passed) { + ICLog.i("SAVER", o1name + ":"); + ICLog.i("SAVER", serializedToDebugString(o1, false)); + ICLog.i("SAVER", o2name + ":"); + ICLog.i("SAVER", serializedToDebugString(o2, false)); + DialogHelper.openFormattedDialog("NEW SAVER TEST FAILED!", "NEW SAVER TEST FAILED!"); + } + return passed; + } + + public static void runJsonSerializationTest(Object json) { + //ICLog.i("SAVER", NativeJSON.stringify(Compiler.assureContextForCurrentThread(), ScriptableObjectHelper.getDefaultScope(), deserialized, null, " ").toString()); + // original (legacy) -> serialized1 (legacy) + Object serialized1 = reSerialize(json, true); + + // original (legacy) -> serialized2 (modern) -> serialized3(modern) -> serialized4 (legacy) + Object serialized2 = reSerialize(json, false); + Object serialized3 = reSerialize(serialized2, false); + Object serialized4 = reSerialize(serialized3, true); + + validateEqual("TEST 1", "ORIGINAL", json, "SERIALIZED1", serialized1); + if (validateEqual("TEST 2", "SERIALIZED2", serialized2, "SERIALIZED3", serialized3)) { + if (validateEqual("TEST 3", "ORIGINAL", json, "SERIALIZED4", serialized4)) { + ICLog.d("SAVER", "SERIALIZED3 " + serializedToDebugString(serialized3, false)); + ICLog.d("SAVER", "ORIGINAL " + serializedToDebugString(json, false)); + } + } + } +} diff --git a/src/main/java/org/innercore/icstd/serialization/OpaqueSerializedData.java b/src/main/java/org/innercore/icstd/serialization/OpaqueSerializedData.java new file mode 100644 index 000000000..8d7521e47 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/OpaqueSerializedData.java @@ -0,0 +1,69 @@ +package org.innercore.icstd.serialization; + +import org.innercore.internal.serialization.JsonIO; + +public class OpaqueSerializedData { + private Object data = null; + private boolean hasData = false; + private boolean isWriting = false; + private int readerCount = 0; + + public OpaqueSerializedData() { + + } + + public boolean isEmpty() { + return !hasData; + } + + public void clear() { + if (isWriting) throw new IllegalStateException("already writing"); + if (readerCount != 0) throw new IllegalStateException("can't write while readers are present"); + hasData = false; + data = null; + } + + private void onEndWrite(Object data) { + this.data = data; + hasData = true; + isWriting = false; + } + + public IScopedWriter clearAndWrite() { + clear(); + return beginWrite(); + } + + public IScopedWriter beginWrite() { + if (isWriting) throw new IllegalStateException("already writing"); + if (readerCount != 0) throw new IllegalStateException("can't write while readers are present"); + if (hasData) throw new IllegalStateException("must be cleaned before writing"); + isWriting = true; + + return new JsonIO.ScopedWriter() { + @Override + public void endScope() { + super.endScope(); + onEndWrite(getResult()); + } + }; + } + + private void onEndRead() { + readerCount--; + } + + public IScopedReader beginRead() { + if (isWriting) throw new IllegalStateException("already writing"); + if (!hasData) throw new IllegalStateException("no data is stored"); + readerCount++; + + return new JsonIO.ScopedReader(data) { + @Override + public void endScope() { + super.endScope(); + onEndRead(); + } + }; + } +} diff --git a/src/main/java/org/innercore/icstd/serialization/ScriptableObjectSerializer.java b/src/main/java/org/innercore/icstd/serialization/ScriptableObjectSerializer.java new file mode 100644 index 000000000..71a0c7b06 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/ScriptableObjectSerializer.java @@ -0,0 +1,144 @@ +package org.innercore.icstd.serialization; + +import com.zhekasmirnov.innercore.api.mod.ScriptableObjectHelper; +import com.zhekasmirnov.innercore.api.runtime.saver.ObjectSaverRegistry; +import org.json.JSONObject; +import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; +import org.mozilla.javascript.Undefined; + +import java.util.ArrayList; +import java.util.List; + +public abstract class ScriptableObjectSerializer implements IObjectSerializer { + private static void writeImpl(IScopedWriter io, Object src, SerializationContext ctx) { + if (src == null || src == Undefined.instance) { + io.endScope(); // null scope + return; + } + if (src instanceof ScriptableObject && !ctx.visitedObjects.add(src)) { + ctx.addError(null, src, "cyclic reference found"); + io.endScope(); + return; + } + Object saverId = ObjectSerializer.getSerializerTypeIdFromObject(src); + if (saverId != null) { + ObjectSerializer.serializeObject(io, src, ctx, /* can fail */ true); + ctx.visitedObjects.remove(src); + return; + } + if (src instanceof NativeArray) { + NativeArray array = (NativeArray) src; + IArrayWriter writer = io.asArray(); + for (int i = 0; i < array.getLength(); i++) { + Object o = array.get(i); + if (ObjectSerializer.isPrimitiveType(o)) { + writer.addAny(o); + } else { + writeImpl(writer.beginScope(), o, ctx); + } + } + io.endScope(); + ctx.visitedObjects.remove(src); + return; + } + if (src instanceof ScriptableObject) { + ScriptableObject scriptable = (ScriptableObject) src; + IObjectWriter writer = io.asObject(); + for (Object id : scriptable.getIds()) { + if (!(id instanceof String)) { + ctx.addError(null, id, "serialized scriptable object can't have integer ids"); + continue; + } + Object o = scriptable.get(id); + if (ObjectSerializer.isPrimitiveType(o)) { + writer.putAny((String) id, o); + } else { + writeImpl(writer.beginScope((String) id), o, ctx); + } + } + io.endScope(); + ctx.visitedObjects.remove(src); + return; + } + ctx.addError(null, src, "invalid object can't be serialized and will be skipped " + (src != null ? src.getClass() : null)); + io.endScope(); + } + + public static void writeScriptable(IScopedWriter writer, Object object, SerializationContext ctx) { + writeImpl(writer, object, ctx); + } + + @Override + public void write(IScopedWriter writer, Object object, SerializationContext ctx, String typeId) { + writeImpl(writer, save(object), ctx); + } + + private static Object readImpl(IScopedReader io, SerializationContext ctx, boolean ignoreThisSaver) { + if (!ignoreThisSaver) { + ObjectSerializer.PackedObjectReader packed = ObjectSerializer.unpackObjectAndTypeId(io, ctx); + if (packed != null) { + Object o = ObjectSerializer.deserializeObjectUnpacked(packed.reader, packed.typeId, Object.class, ctx, /* can fail */ true); + packed.endScope(); + return o; + } + } + if (io.isArray()) { + IArrayReader reader = io.asArray(); + List array = new ArrayList<>(reader.size()); + for (int i = 0; i < reader.size(); i++) { + if (reader.getValueType(i).isScope()) { + array.add(readImpl(reader.beginScope(i), ctx, false)); + } else { + array.add(reader.getAny(i, null)); + } + } + io.endScope(); + return ScriptableObjectHelper.createArray(array); + } + if (io.isObject()) { + IObjectReader reader = io.asObject(); + ScriptableObject object = ScriptableObjectHelper.createEmpty(); + for (String key : reader.iterKeys()) { + if (reader.getValueType(key).isScope()) { + object.put(key, object, readImpl(reader.beginScope(key), ctx, false)); + } else { + object.put(key, object, reader.getAny(key, null)); + } + } + io.endScope(); + return object; + } + if (io.isEmpty()) { + io.endScope(); + return null; + } + ctx.addError(null, null, "scope is not an object nor an array"); + io.endScope(); + return null; + } + + public static Object readScriptable(IScopedReader reader, SerializationContext ctx) { + return readImpl(reader, ctx, false); + } + + @Override + public Object read(IScopedReader reader, SerializationContext ctx, String typeId) { + Object result = readImpl(reader, ctx, true); + if (result instanceof ScriptableObject) { + Object o = read((ScriptableObject) result); + if (o instanceof Scriptable && ObjectSerializer.getSerializerTypeIdFromObject(o) == null) { + Scriptable s = (Scriptable) o; + s.put(ObjectSaverRegistry.PROPERTY_SAVER_ID, s, typeId); + } + return o; + } else { + ctx.addError(null, result, "scriptable object saver can't read non-scriptable"); + return null; + } + } + + public abstract Object read(ScriptableObject input); + public abstract ScriptableObject save(Object input); +} diff --git a/src/main/java/org/innercore/icstd/serialization/SerializationContext.java b/src/main/java/org/innercore/icstd/serialization/SerializationContext.java new file mode 100644 index 000000000..eacd3f8db --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/SerializationContext.java @@ -0,0 +1,80 @@ +package org.innercore.icstd.serialization; + +import com.zhekasmirnov.innercore.api.log.ICLog; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +public class SerializationContext { + public int recursionDepth = 0; + public boolean hasLegacyFormat = true; + public boolean forceLegacyFormat = false; + public HashSet visitedObjects = new HashSet<>(); + + public static class ErrorRecord { + private String message; + private Object object; + private Throwable exception; + + private ErrorRecord(String message, Object o, Throwable exception) { + this.message = message; + object = o; + this.exception = exception; + } + + public String getMessage() { return message; } + public Object getObject() { return object; } + public Throwable getException() { return exception; } + + public String objectToString(boolean inline, int inlineLimit) { + String str = object != null ? object.toString() : "null"; + if (inlineLimit > 0 && str.length() > inlineLimit) { + str = str.substring(0, inlineLimit); + } + return str; + } + + public boolean isEmpty() { + return object == null && exception == null && (message == null || message.isEmpty()); + } + + public String formatMsg(boolean inline, int inlineLimit, int stackLimit) { + // TODO: improve this + boolean hasMessage = !(message == null || message.isEmpty()); + if (exception == null && object == null && !hasMessage) { + return "*no message*"; + } + StringBuilder fmt = new StringBuilder(); + if (hasMessage) { + fmt.append(message); + } + if (object != null) { + fmt.append(" at ").append(objectToString(inline, inlineLimit)); + } + if (exception != null) { + fmt.append(" with ").append(ICLog.exceptionToString(exception, inline, stackLimit >= 0, stackLimit, -1)); + } + return fmt.toString(); + } + } + + public List errorsList = null; + public List verboseMsgList = null; + public boolean logWhenAddingErrors = true; + + public void addError(Throwable err, Object o, String msg) { + if (errorsList == null) errorsList = new ArrayList<>(); + ErrorRecord r = new ErrorRecord(msg, o, err); + if (r.isEmpty()) + r.message = "*no message*"; + if (r.exception == null) + r.exception = new RuntimeException(); + if (logWhenAddingErrors) + ICLog.i("Serializer", "error " + r.formatMsg(true, 256, 3)); + errorsList.add(r); + } + public void addError(String msg) { addError(null, null, msg); } + public boolean hasErrors() { return errorsList != null && !errorsList.isEmpty(); } +} diff --git a/src/main/java/org/innercore/icstd/serialization/SerializationMethodsComponent.java b/src/main/java/org/innercore/icstd/serialization/SerializationMethodsComponent.java new file mode 100644 index 000000000..d4c430f20 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/SerializationMethodsComponent.java @@ -0,0 +1,6 @@ +package org.innercore.icstd.serialization; + +public interface SerializationMethodsComponent { + void read(IScopedReader io, SerializationContext ctx, String typeId); + void write(IScopedWriter io, SerializationContext ctx, String typeId); +} diff --git a/src/main/java/org/innercore/icstd/serialization/SerializationUtils.java b/src/main/java/org/innercore/icstd/serialization/SerializationUtils.java new file mode 100644 index 000000000..655e6fd54 --- /dev/null +++ b/src/main/java/org/innercore/icstd/serialization/SerializationUtils.java @@ -0,0 +1,62 @@ +package org.innercore.icstd.serialization; + +public class SerializationUtils { + public static void readerToWriter(IScopedReader src, IScopedWriter dst) { + if (src.isObject()) { + IObjectReader srcObj = src.asObject(); + IObjectWriter dstObj = dst.asObject(); + boolean anySupported = srcObj.isGetAnySupported() && dstObj.isPutAnySupported(); + for (String key : srcObj.iterKeys()) { + ESerializedType type = srcObj.getValueType(key); + if (type.isScope()) { + readerToWriter(srcObj.beginScope(key), dstObj.beginScope(key)); + continue; + } + if (anySupported) { + dstObj.putAny(key, srcObj.getAny(key, null)); + continue; + } + switch (type) { + case INT: dstObj.putInt(key, srcObj.getInt(key, 0)); break; + case LONG: dstObj.putLong(key, srcObj.getLong(key, 0)); break; + case SHORT: dstObj.putShort(key, srcObj.getShort(key, (short) 0)); break; + case BYTE: dstObj.putByte(key, srcObj.getByte(key, (byte) 0)); break; + case FLOAT: dstObj.putFloat(key, srcObj.getFloat(key, 0)); break; + case DOUBLE: dstObj.putDouble(key, srcObj.getDouble(key, 0)); break; + case BOOLEAN: dstObj.putBoolean(key, srcObj.getBoolean(key, false)); break; + case STRING: dstObj.putString(key, srcObj.getString(key, "")); break; + default: throw new IllegalStateException("unsupported type " + type); + } + } + } else if (src.isArray()) { + IArrayReader srcArr = src.asArray(); + IArrayWriter dstArr = dst.asArray(); + boolean anySupported = srcArr.isGetAnySupported() && dstArr.isPutAnySupported(); + int size = srcArr.size(); + for (int i = 0; i < size; i++) { + ESerializedType type = srcArr.getValueType(i); + if (type.isScope()) { + readerToWriter(srcArr.beginScope(i), dstArr.beginScope()); + continue; + } + if (anySupported) { + dstArr.addAny(srcArr.getAny(i, null)); + continue; + } + switch (type) { + case INT: dstArr.addInt(srcArr.getInt(i, 0)); break; + case LONG: dstArr.addLong(srcArr.getLong(i, 0)); break; + case SHORT: dstArr.addShort(srcArr.getShort(i, (short) 0)); break; + case BYTE: dstArr.addByte(srcArr.getByte(i, (byte) 0)); break; + case FLOAT: dstArr.addFloat(srcArr.getFloat(i, 0)); break; + case DOUBLE: dstArr.addDouble(srcArr.getDouble(i, 0)); break; + case BOOLEAN: dstArr.addBoolean(srcArr.getBoolean(i, false)); break; + case STRING: dstArr.addString(srcArr.getString(i, "")); break; + default: throw new IllegalStateException("unsupported type " + type); + } + } + } + src.endScope(); + dst.endScope(); + } +} diff --git a/src/main/java/org/innercore/icstd/updatable/ScriptUpdatable.java b/src/main/java/org/innercore/icstd/updatable/ScriptUpdatable.java new file mode 100644 index 000000000..5e3095d4b --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/ScriptUpdatable.java @@ -0,0 +1,38 @@ +package org.innercore.icstd.updatable; + +import com.zhekasmirnov.innercore.mod.executable.Compiler; +import org.innercore.icstd.script.ScriptObjectComponent; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +class ScriptUpdatable implements UpdatableComponent, ScriptObjectComponent { + UpdatableSchedulerScope scope; + Scriptable object; + Function function; + Object[] args; + Scriptable parentScope; + + ScriptUpdatable(UpdatableSchedulerScope scope, ScriptableObject object, Function function) { + this.scope = scope; + this.object = object; + this.parentScope = function.getParentScope(); + if (this.object == null) + this.object = parentScope; + this.function = function; + args = scope.scriptDtArg; + } + + @Override + public void update(int dt) { + if (scope.scriptCtx == null) + scope.scriptCtx = Compiler.assureContextForCurrentThread(); + args[0] = dt; + function.call(scope.scriptCtx, parentScope, object, args); + } + + @Override + public Object getScriptObject() { + return object; + } +} \ No newline at end of file diff --git a/src/main/java/org/innercore/icstd/updatable/UpdatableComponent.java b/src/main/java/org/innercore/icstd/updatable/UpdatableComponent.java new file mode 100644 index 000000000..0a55af985 --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/UpdatableComponent.java @@ -0,0 +1,5 @@ +package org.innercore.icstd.updatable; + +public interface UpdatableComponent { + void update(int dt); +} diff --git a/src/main/java/org/innercore/icstd/updatable/UpdatableHandle.java b/src/main/java/org/innercore/icstd/updatable/UpdatableHandle.java new file mode 100644 index 000000000..f1b1ca445 --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/UpdatableHandle.java @@ -0,0 +1,43 @@ +package org.innercore.icstd.updatable; + +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.UpdatableHandleBindGenBase; + +@ApiBinding("UpdatableHandle") +public class UpdatableHandle extends UpdatableHandleBindGenBase { + private long handle; + + @ApiBinding + public UpdatableHandle(long handle) { + this.handle = handle; + } + + @ApiBinding + public long getHandle() { return handle; } + + /* @ApiBinding + public long replaceUpdatableHandle(long handle) { + long last = this.handle; + this.handle = handle; + return last; + } */ + + @ApiBinding + public boolean isAlive() { return handle != -1 && UpdatableScheduler.isRawHandleAlive(handle); } + @ApiBinding public UpdatableHandle schedule(int tickIn, int tickUntil) { UpdatableScheduler.scheduleByRawHandle(handle, tickIn, tickUntil); return this; } + @ApiBinding public UpdatableHandle schedule(int tickIn) { UpdatableScheduler.scheduleByRawHandle(handle, tickIn); return this; } + @ApiBinding public UpdatableHandle setRecurringSchedule(int tickIn, int tickUntil) { UpdatableScheduler.setRecurringScheduleByRawHandle(handle, tickIn, tickUntil); return this; } + @ApiBinding public UpdatableHandle setRecurringSchedule(int tickIn) { UpdatableScheduler.setRecurringScheduleByRawHandle(handle, tickIn); return this;} + @ApiBinding public UpdatableHandle clearSchedule() { UpdatableScheduler.clearScheduleByRawHandle(handle); return this; } + + public UpdatableComponent getObject() { return UpdatableScheduler.getByRawHandle(handle); } + @ApiBinding("getObject") public Object getObjectForScript() { return UpdatableScheduler.getByRawHandleForScript(handle); } + @ApiBinding public UpdatableSchedulerScope getScope() { return UpdatableScheduler.getScopeByRawHandle(handle); } + + @ApiBinding + public boolean destroy() { + boolean result = UpdatableScheduler.destroyRawHandle(handle); + handle = -1; + return result; + } +} diff --git a/src/main/java/org/innercore/icstd/updatable/UpdatableScheduler.java b/src/main/java/org/innercore/icstd/updatable/UpdatableScheduler.java new file mode 100644 index 000000000..d5ebe2694 --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/UpdatableScheduler.java @@ -0,0 +1,124 @@ +package org.innercore.icstd.updatable; + +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.UpdatableSchedulerBindGenBase; +import com.zhekasmirnov.innercore.api.runtime.Updatable; +import org.innercore.icstd.annotations.ArgOverload; +import org.innercore.icstd.script.ScriptObjectComponent; +import org.innercore.icstd.updatable.impl.UpdatableSchedulerScopeImpl; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +import java.util.Arrays; + +@ApiBinding("UpdatableScheduler") +public class UpdatableScheduler extends UpdatableSchedulerBindGenBase { + private static UpdatableSchedulerScope[] scopes = new UpdatableSchedulerScope[4]; + + @ApiBinding + public static void clearAllImpl(int scopeId) { + UpdatableSchedulerScopeImpl.clearAllImpl(scopeId); + } + + @ApiBinding + public static int createNewScopeImpl(String scopeName) { + return UpdatableSchedulerScopeImpl.createNewScopeImpl(scopeName); + } + + @ApiBinding + public static long createHandle(int scopeId) { + return UpdatableSchedulerScopeImpl.create(scopeId); + } + + @ApiBinding + public static boolean destroyHandle(long handleId) { + return UpdatableSchedulerScopeImpl.destroy(handleId); + } + + @ApiBinding + public static boolean isRawHandleAlive(long handleId) { + return UpdatableSchedulerScopeImpl.isRawHandleAlive(handleId); + } + + @ApiBinding + public static void scheduleByRawHandle(long handleId, int tickAt, @ArgOverload("-1") int tickUntil) { + UpdatableSchedulerScopeImpl.scheduleByRawHandle(handleId, tickAt, tickUntil); + } + + @ApiBinding + public static void setRecurringScheduleByRawHandle(long handleId, int tickAt, @ArgOverload("-1") int tickUntil) { + UpdatableSchedulerScopeImpl.setRecurringScheduleByRawHandle(handleId, tickAt, tickUntil); + } + + @ApiBinding + public static void clearScheduleByRawHandle(long handleId) { + UpdatableSchedulerScopeImpl.clearScheduleByRawHandle(handleId); + } + + @ApiBinding + public static void setCurrentTickImpl(int scopeId, int tickNo) { + UpdatableSchedulerScopeImpl.setCurrentTickImpl(scopeId, tickNo); + } + + + @ApiBinding("createNewScope") + public static UpdatableSchedulerScope createNewScope(String name) { + int scopeId = createNewScopeImpl(name); + if (scopes.length <= scopeId) { + scopes = Arrays.copyOf(scopes, Math.max(scopeId + 2, scopes.length * 3 / 2 + 1)); + } + UpdatableSchedulerScope scope = scopes[scopeId] = new UpdatableSchedulerScope(scopeId); + scope.name = name; + scope.setCurrentTick(0); + return scopes[scopeId]; + } + + @ApiBinding("getMainServerScope") + public static UpdatableSchedulerScope getMainServerScope() { return Updatable.getForServer().updatableScope; } + @ApiBinding("getMainClientScope") + public static UpdatableSchedulerScope getMainClientScope() { return Updatable.getForClient().updatableScope; } + + public static UpdatableHandle createOnServer(UpdatableComponent updatable) { return getMainServerScope().create(updatable); } + public static UpdatableHandle createOnClient(UpdatableComponent updatable) { return getMainClientScope().create(updatable); } + + @ApiBinding("createOnServer") + public static UpdatableHandle createOnServerForScript(ScriptableObject object, Function function) { return getMainServerScope().createForScript(object, function); } + @ApiBinding("createOnClient") + public static UpdatableHandle createOnClientForScript(ScriptableObject object, Function function) { return getMainClientScope().createForScript(object, function); } + @ApiBinding("createOnServer") + public static UpdatableHandle createOnServerForScript(Scriptable objectOrFunction) { return getMainServerScope().createForScript(objectOrFunction); } + @ApiBinding("createOnClient") + public static UpdatableHandle createOnClientForScript(Scriptable objectOrFunction) { return getMainClientScope().createForScript(objectOrFunction); } + + + @ApiBinding + public static boolean destroyRawHandle(long handle) { + return destroyHandle(handle); + // do not remove anything from updatable list here, as it might interfere with tick execution (removing updatable during tick) + } + + public static UpdatableComponent getByRawHandle(long handle) { + if (isRawHandleAlive(handle)) { + int idx = (int) (handle & 0xFFFFFFL); + int scopeId = (int) (handle >> 32L); + return scopes[scopeId].list[idx]; + } + return null; + } + + @ApiBinding("getByRawHandle") + public static Object getByRawHandleForScript(long handle) { + UpdatableComponent updatable = getByRawHandle(handle); + return updatable instanceof ScriptObjectComponent ? ((ScriptObjectComponent) updatable).getScriptObject() : null; + } + + @ApiBinding + public static UpdatableSchedulerScope getScopeByRawHandle(long handle) { + if (isRawHandleAlive(handle)) { + int scopeId = (int) (handle >> 32L); + return scopes[scopeId]; + } + return null; + } +} diff --git a/src/main/java/org/innercore/icstd/updatable/UpdatableSchedulerScope.java b/src/main/java/org/innercore/icstd/updatable/UpdatableSchedulerScope.java new file mode 100644 index 000000000..fbbe3ce01 --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/UpdatableSchedulerScope.java @@ -0,0 +1,167 @@ +package org.innercore.icstd.updatable; + +import com.zhekasmirnov.apparatus.minecraft.enums.EnumsContainer; +import org.innercore.icstd.annotations.ApiBinding; +import com.zhekasmirnov.innercore.api.gen.UpdatableSchedulerScopeBindGenBase; +import org.innercore.icstd.updatable.impl.UpdatableSchedulerScopeImpl; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.Function; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.ScriptableObject; + +import java.util.Arrays; +import java.util.List; + +@ApiBinding +public class UpdatableSchedulerScope extends UpdatableSchedulerScopeBindGenBase { + public final int scopeId; + + // internals + String name; + int curTick = 0; + UpdatableComponent[] list = new UpdatableComponent[0]; + int[] tickingArray = new int[128]; + Context scriptCtx = null; + Object[] scriptDtArg = new Object[1]; + + UpdatableSchedulerScope(int id) { + this.scopeId = id; + } + + @ApiBinding + public void setCurrentTick(int tickNo) { + UpdatableScheduler.setCurrentTickImpl(scopeId, tickNo); + curTick = tickNo; + } + + @ApiBinding + public int getCurrentTick() { + return curTick; + } + + public long createRawHandle(UpdatableComponent updatable) { + synchronized (this) { + long handle = UpdatableScheduler.createHandle(scopeId); + int idx = (int) (handle & 0xFFFFFFL); + if (list.length <= idx) { + list = Arrays.copyOf(list, Math.max(idx + 16, list.length * 3 / 2 + 1)); + } + list[idx] = updatable; + return handle; + } + } + + public UpdatableHandle create(UpdatableComponent updatable) { + return new UpdatableHandle(createRawHandle(updatable)); + } + + @ApiBinding("createRawHandle") + public long createRawHandleForScript(ScriptableObject object, Function function) { + return createRawHandle(new ScriptUpdatable(this, object, function)); + } + + @ApiBinding("create") + public UpdatableHandle createForScript(ScriptableObject object, Function function) { + return create(new ScriptUpdatable(this, object, function)); + } + + @ApiBinding("create") + public UpdatableHandle createForScript(Scriptable objectOrFunction) { + if (objectOrFunction instanceof Function) { + return createForScript(null, (Function) objectOrFunction); + } else { + if (!(objectOrFunction instanceof ScriptableObject)) { + throw new IllegalArgumentException("cannot add script updatable, parameter must be script object or function, got " + objectOrFunction); + } + ScriptableObject obj = (ScriptableObject) objectOrFunction; + Object update = obj.get("update", obj); + if (!(update instanceof Function)) { + throw new IllegalArgumentException("cannot add updatable object: .update must be a function, not " + (update != null ? update.getClass() : null) + " " + update); + } + UpdatableHandle handle = createForScript(obj, (Function) update); + obj.put("_handle", obj, handle); + return handle; + } + } + + @ApiBinding("clearAll") + public void clearAll() { + synchronized (this) { + UpdatableScheduler.clearAllImpl(scopeId); + list = new UpdatableComponent[0]; + tickingArray = new int[128]; + } + } + + private static void sneakyThrow(Throwable e) throws E { + throw (E) e; + } + + public void executeTickForScope(int tickNo, int timeLimitUs, List outErrorList) { + synchronized (this) { // synchronize with create + if (curTick + 1 != tickNo && curTick != tickNo) + throw new IllegalArgumentException("UpdatableScheduler.executeTickForScope tick mismatch " + curTick + "/" + tickNo); + setCurrentTick(tickNo); + tickingArray = nativeExecuteTickForScope(scopeId, tickingArray); + } + UpdatableComponent[] list = this.list; + int[] toTick = tickingArray; + int toTickEnd = toTick[0]; + int toTickExhaustiveEnd = toTick[1]; + int i; + // if (toTickEnd != 2 || toTickExhaustiveEnd != toTick.length) + // ICLog.d("UpdatableScheduler", "toTick=" + Arrays.toString(toTick)); + + // main tick part + Throwable stoppedOnError = null; + for (i = 2; i < toTickEnd; i += 2) { + try { + for (; i < toTickEnd; i += 2) + list[toTick[i]].update(toTick[i + 1]); + } catch (Throwable t) { + if (outErrorList != null) { + outErrorList.add(t); + } else { + stoppedOnError = t; + break; + } + } + } + + // exhaustive tick part + if (stoppedOnError == null) { + for (i = toTick.length - 2; i >= toTickExhaustiveEnd; i -= 2) { + try { + for (; i >= toTickExhaustiveEnd; i -= 2) + list[toTick[i]].update(toTick[i + 1]); + } catch (Throwable t) { + if (outErrorList != null) { + outErrorList.add(t); + } else { + stoppedOnError = t; + break; + } + } + } + + } + + nativeMarkTicked(scopeId, toTick, i + 2, toTick.length); + if (stoppedOnError != null) { + sneakyThrow(stoppedOnError); + } + } + + @ApiBinding("executeTickForScope") + public void executeTickForScope(int tickNo, int timeLimitUs) { + executeTickForScope(tickNo, timeLimitUs, null); + } + + private static int[] nativeExecuteTickForScope(int scopeId, int[] toTick) { + return UpdatableSchedulerScopeImpl.nativeExecuteTickForScope(scopeId, toTick); + } + + private static void nativeMarkTicked(int scopeId, int[] toTick, int begin, int end) { + UpdatableSchedulerScopeImpl.nativeMarkTicked(scopeId, toTick, begin, end); + } +} diff --git a/src/main/java/org/innercore/icstd/updatable/impl/UpdatableRef.java b/src/main/java/org/innercore/icstd/updatable/impl/UpdatableRef.java new file mode 100644 index 000000000..89071826e --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/impl/UpdatableRef.java @@ -0,0 +1,58 @@ +package org.innercore.icstd.updatable.impl; + +/** + * A handle that encodes an index (24 bits), a generation (8 bits), and a scope ID (16 bits) + * into a single 64‑bit long. + */ +final class UpdatableRef { + private final long handle; + + // Masks for bit fields (assumed order: idx in low 24 bits, gen in next 8, scope in next 16) + private static final long IDX_MASK = 0xFFFFFFL; + private static final long GEN_MASK = 0xFFL << 24; + private static final long SCOPE_MASK = 0xFFFFL << 32; + + public UpdatableRef() { + this.handle = ~0L; // all bits set (~0u in C++) + } + + public UpdatableRef(long handle) { + this.handle = handle; + } + + public UpdatableRef(int idx, int gen, short scope) { + this.handle = (idx & IDX_MASK) + | ((gen & 0xFFL) << 24) + | ((long)(scope & 0xFFFF) << 32); + } + + public long getHandle() { return handle; } + + public int getIdx() { + return (int)(handle & IDX_MASK); + } + + public int getGen() { + return (int)((handle >> 24) & 0xFF); + } + + public short getScope() { + return (short)((handle >> 32) & 0xFFFF); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UpdatableRef)) return false; + return handle == ((UpdatableRef) o).handle; + } + + @Override + public int hashCode() { + return Long.hashCode(handle); + } + + // Comparison operators (used by containers) + public boolean lt(UpdatableRef rhs) { return handle < rhs.handle; } + public boolean gt(UpdatableRef rhs) { return handle > rhs.handle; } +} \ No newline at end of file diff --git a/src/main/java/org/innercore/icstd/updatable/impl/UpdatableScheduler.java b/src/main/java/org/innercore/icstd/updatable/impl/UpdatableScheduler.java new file mode 100644 index 000000000..e9e757f33 --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/impl/UpdatableScheduler.java @@ -0,0 +1,262 @@ +package org.innercore.icstd.updatable.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +class UpdatableScheduler { + final short scopeId; + final ReentrantReadWriteLock extLock = new ReentrantReadWriteLock(); + int curTick = 0; + + // Internal record + private static class UpdatableRecord { + boolean isAlive; + boolean isTouchedThisTick; + int generation; // uint8_t, but range 0‑255 + + // Union: scheduledAtBegin / freeListNext + int scheduledAtBegin; // uint32_t + int freeListNext; // used only when record is in free list + int scheduledAtEnd; // uint32_t + int tickedAt; // uint32_t (~0u initially) + + int rescheduleOnTickMin; // uint32_t + int rescheduleOnTickMax; // uint32_t + + void reset() { + isAlive = true; + isTouchedThisTick = false; + tickedAt = ~0; // ~0u in C++ (all bits 1) + scheduledAtBegin = scheduledAtEnd = ~0; + rescheduleOnTickMin = rescheduleOnTickMax = 0; + } + } + + private final List updatables = new ArrayList<>(); + private int freeListHead = -1; + private int freeListTail = -1; + private int freeListCnt = 0; + + public UpdatableScheduler(short scopeId) { + this.scopeId = scopeId; + } + + /** + * Creates a new updatable and returns its handle. + * Reuses a record from the free list if freeListCnt > 64, + * otherwise appends a new one. + */ + public UpdatableRef create() { + extLock.writeLock().lock(); + try { + if (freeListCnt > 64) { + int idx = freeListHead; + UpdatableRecord u = updatables.get(idx); + freeListHead = u.freeListNext; + freeListCnt--; + u.reset(); + u.generation++; // overflow is acceptable (wraps like uint8_t) + return new UpdatableRef(idx, u.generation & 0xFF, scopeId); + } else { + int idx = updatables.size(); + UpdatableRecord u = new UpdatableRecord(); + u.reset(); + u.generation = 0; + updatables.add(u); + return new UpdatableRef(idx, 0, scopeId); + } + } finally { + extLock.writeLock().unlock(); + } + } + + /** + * Checks whether a handle is still valid and alive. + */ + public boolean exists(UpdatableRef ref) { + extLock.readLock().lock(); + try { + int idx = ref.getIdx(); + if (idx >= updatables.size()) return false; + UpdatableRecord u = updatables.get(idx); + return u.isAlive && (ref.getGen() == (u.generation & 0xFF)); + } finally { + extLock.readLock().unlock(); + } + } + + /** + * Destroys an alive updatable, returning true on success. + */ + public boolean destroy(UpdatableRef ref) { + extLock.writeLock().lock(); + try { + if (!exists(ref)) return false; + int idx = ref.getIdx(); + UpdatableRecord u = updatables.get(idx); + if (freeListCnt > 0) { + updatables.get(freeListTail).freeListNext = idx; + } else { + freeListHead = idx; + } + freeListTail = idx; + freeListCnt++; + u.isAlive = false; + return true; + } finally { + extLock.writeLock().unlock(); + } + } + + /** + * Schedules an updatable to be ticked between tick_in and tick_until (inclusive) + * relative to current tick. Only changes the schedule if it would be sooner. + */ + public void schedule(UpdatableRef ref, int tickIn, int tickUntil) { + if (tickUntil < tickIn) tickUntil = tickIn; + extLock.readLock().lock(); // C++ uses shared_lock for schedule + try { + if (!exists(ref) || tickIn <= 0) return; + UpdatableRecord u = updatables.get(ref.getIdx()); + u.scheduledAtBegin = u.scheduledAtBegin < 0 ? curTick + tickIn : Math.min(u.scheduledAtBegin, curTick + tickIn); + u.scheduledAtEnd = u.scheduledAtEnd < 0 ? curTick + tickUntil : Math.min(u.scheduledAtEnd, curTick + tickUntil); + } finally { + extLock.readLock().unlock(); + } + } + + /** + * Sets a recurring schedule that will be re‑applied after each tick. + */ + public void setRecurringSchedule(UpdatableRef ref, int tickIn, int tickUntil) { + if (tickUntil < tickIn) tickUntil = tickIn; + extLock.readLock().lock(); + try { + if (!exists(ref)) return; + UpdatableRecord u = updatables.get(ref.getIdx()); + u.rescheduleOnTickMin = tickIn; + u.rescheduleOnTickMax = tickUntil; + } finally { + extLock.readLock().unlock(); + } + } + + /** + * Clears the immediate schedule without changing the recurring settings. + */ + public void clearSchedule(UpdatableRef ref) { + extLock.readLock().lock(); + try { + if (!exists(ref)) return; + UpdatableRecord u = updatables.get(ref.getIdx()); + u.tickedAt = ~0; + u.scheduledAtBegin = u.scheduledAtEnd = ~0; + } finally { + extLock.readLock().unlock(); + } + } + + /** + * Marks all records as dead and builds a free list of the entire array. + */ + public void clearAll() { + extLock.writeLock().lock(); + try { + int size = updatables.size(); + for (int i = 0; i < size; i++) { + UpdatableRecord u = updatables.get(i); + u.isAlive = false; + u.freeListNext = i + 1; // last element may point to garbage – never accessed + } + freeListHead = 0; + freeListTail = size - 1; + freeListCnt = size; + } finally { + extLock.writeLock().unlock(); + } + } + + public int getAliveCnt() { + extLock.readLock().lock(); + try { + return updatables.size() - freeListCnt; + } finally { + extLock.readLock().unlock(); + } + } + + // --------------------------------------------------------- + // Tick processing (was JNI, now pure Java) + // --------------------------------------------------------- + + /** + * Called after a tick has finished – re‑schedules the record according to its + * recurring settings (if any). + */ + private void onTicked(UpdatableRecord u) { + u.scheduledAtBegin = (u.rescheduleOnTickMin == 0) ? ~0 : curTick + u.rescheduleOnTickMin; + u.scheduledAtEnd = (u.rescheduleOnTickMax == 0) ? ~0 : curTick + u.rescheduleOnTickMax; + u.tickedAt = curTick; + } + + /** + * Fills the provided array with indices of updatables that should be ticked now + * (placed at the beginning) or later (placed at the end). The array must be + * large enough. The first two elements hold the “now” and “exhaustive” positions + * after the call. This mimics the C++ JNI helper. + */ + public void scheduleAtTickForJava(int[] tickArr) { + extLock.writeLock().lock(); // original does not lock – we lock for safety + try { + int nowPos = 2; // start after meta fields + int exhaustivePos = tickArr.length; // start from the end + + for (int i = 0; i < updatables.size(); i++) { + UpdatableRecord u = updatables.get(i); + if (!u.isAlive) continue; + + u.isTouchedThisTick = true; // mark for later verification + + if (u.scheduledAtEnd == curTick) { + // Should be ticked right now + tickArr[nowPos] = i; + tickArr[nowPos + 1] = (u.tickedAt > curTick) ? 0 : curTick - u.tickedAt; + nowPos += 2; + onTicked(u); + } else if (u.scheduledAtBegin >= curTick) { + // Will be ticked in the future + exhaustivePos -= 2; + tickArr[exhaustivePos] = i; + tickArr[exhaustivePos + 1] = (u.tickedAt > curTick) ? 0 : curTick - u.tickedAt; + } + } + + // Store the current positions for the caller + tickArr[0] = nowPos; + tickArr[1] = exhaustivePos; + } finally { + extLock.writeLock().unlock(); + } + } + + /** + * Marks a batch of indices as ticked. Only affects records that were touched + * during the current scheduling round and are still alive. + */ + public void markBatchTickedForJava(int[] tickedIndices, int begin, int end) { + extLock.writeLock().lock(); + try { + for (int i = begin; i < end; i += 2) { + int idx = tickedIndices[i]; + if (idx < 0 || idx >= updatables.size()) continue; + UpdatableRecord u = updatables.get(idx); + if (u.isTouchedThisTick && u.isAlive) { + onTicked(u); + } + } + } finally { + extLock.writeLock().unlock(); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/innercore/icstd/updatable/impl/UpdatableSchedulerScopeImpl.java b/src/main/java/org/innercore/icstd/updatable/impl/UpdatableSchedulerScopeImpl.java new file mode 100644 index 000000000..3f85fcaaf --- /dev/null +++ b/src/main/java/org/innercore/icstd/updatable/impl/UpdatableSchedulerScopeImpl.java @@ -0,0 +1,174 @@ +package org.innercore.icstd.updatable.impl; + +import java.util.ArrayList; +import java.util.List; + +/** + * Static owner of all scopes (replaces global g_updatable_scopes). + * Provides the high‑level API that matches the original CGB‑bindable functions. + */ +public class UpdatableSchedulerScopeImpl { + private static final List scopes = new ArrayList<>(); + + /** + * Creates a new scope and returns its ID. + */ + public static int createNewScopeImpl(String scopeName) { + synchronized (scopes) { + int scope = scopes.size(); + scopes.add(new UpdatableScheduler((short) scope)); + return scope; + } + } + + /** + * Clears all updatables in a scope. + */ + public static void clearAllImpl(int scope) { + UpdatableScheduler sched; + synchronized (scopes) { + if (scope < 0 || scope >= scopes.size()) return; + sched = scopes.get(scope); + } + sched.extLock.writeLock().lock(); + try { + sched.clearAll(); + } finally { + sched.extLock.writeLock().unlock(); + } + } + + /** + * Creates a new updatable in the given scope, returning the packed handle. + */ + public static long create(int scope) { + UpdatableScheduler sched; + synchronized (scopes) { + if (scope < 0 || scope >= scopes.size()) return -1; + sched = scopes.get(scope); + } + return sched.create().getHandle(); + } + + /** + * Destroys an updatable identified by its packed handle. + */ + public static boolean destroy(long handle) { + UpdatableRef ref = new UpdatableRef(handle); + short scope = ref.getScope(); + synchronized (scopes) { + if (scope < 0 || scope >= scopes.size()) return false; + return scopes.get(scope).destroy(ref); + } + } + + /** + * Checks whether a handle is still alive. + */ + public static boolean isRawHandleAlive(long handle) { + UpdatableRef ref = new UpdatableRef(handle); + short scope = ref.getScope(); + synchronized (scopes) { + if (scope < 0 || scope >= scopes.size()) return false; + return scopes.get(scope).exists(ref); + } + } + + /** + * Schedules an updatable for a one‑shot tick window. + */ + public static void scheduleByRawHandle(long handle, int tickAt, int tickUntil) { + if (tickUntil < tickAt) tickUntil = tickAt; + UpdatableRef ref = new UpdatableRef(handle); + short scope = ref.getScope(); + UpdatableScheduler sched; + synchronized (scopes) { + if (scope < 0 || scope >= scopes.size()) return; + sched = scopes.get(scope); + } + sched.schedule(ref, tickAt, tickUntil); + } + + /** + * Sets a recurring schedule for an updatable. + */ + public static void setRecurringScheduleByRawHandle(long handle, int tickAt, int tickUntil) { + if (tickUntil < tickAt) tickUntil = tickAt; + UpdatableRef ref = new UpdatableRef(handle); + short scope = ref.getScope(); + UpdatableScheduler sched; + synchronized (scopes) { + if (scope < 0 || scope >= scopes.size()) return; + sched = scopes.get(scope); + } + sched.setRecurringSchedule(ref, tickAt, tickUntil); + } + + /** + * Clears the immediate schedule of an updatable (keeps recurring settings). + */ + public static void clearScheduleByRawHandle(long handle) { + UpdatableRef ref = new UpdatableRef(handle); + short scope = ref.getScope(); + UpdatableScheduler sched; + synchronized (scopes) { + if (scope < 0 || scope >= scopes.size()) return; + sched = scopes.get(scope); + } + sched.clearSchedule(ref); + } + + /** + * Sets the current tick counter for a scope. + */ + public static void setCurrentTickImpl(int scopeId, int tickNo) { + UpdatableScheduler sched; + synchronized (scopes) { + if (scopeId < 0 || scopeId >= scopes.size()) return; + sched = scopes.get(scopeId); + } + sched.extLock.writeLock().lock(); + try { + sched.curTick = tickNo; + } finally { + sched.extLock.writeLock().unlock(); + } + } + + /** + * Replaces the native JNI method – executes a tick for a scope and returns + * an int array with indices of updatables that must be processed now and later. + */ + public static int[] nativeExecuteTickForScope(int scopeId, int[] retainArr) { + UpdatableScheduler sched; + synchronized (scopes) { + if (scopeId < 0 || scopeId >= scopes.size()) return new int[0]; + sched = scopes.get(scopeId); + } + + sched.extLock.writeLock().lock(); + try { + int alive = sched.getAliveCnt(); + int needed = alive * 2 + 4; + if (retainArr == null || retainArr.length < needed) { + retainArr = new int[needed + 64]; // generous extra + } + sched.scheduleAtTickForJava(retainArr); + return retainArr; + } finally { + sched.extLock.writeLock().unlock(); + } + } + + /** + * Replaces the native JNI method – marks a range of indices as ticked. + */ + public static void nativeMarkTicked(int scopeId, int[] arr, int begin, int end) { + UpdatableScheduler sched; + synchronized (scopes) { + if (scopeId < 0 || scopeId >= scopes.size()) return; + sched = scopes.get(scopeId); + } + sched.markBatchTickedForJava(arr, begin, end); + } +} diff --git a/src/main/java/org/innercore/icstd/world/DestroyBlockContext.java b/src/main/java/org/innercore/icstd/world/DestroyBlockContext.java new file mode 100644 index 000000000..5819c6f32 --- /dev/null +++ b/src/main/java/org/innercore/icstd/world/DestroyBlockContext.java @@ -0,0 +1,9 @@ +package org.innercore.icstd.world; + +import org.innercore.icstd.common.BlockPos; + +public class DestroyBlockContext { + public BlockPos pos = null; + public long playerUid = -1; + public boolean isDropAllowed = true; +} diff --git a/src/main/java/org/innercore/internal/serialization/JsonIO.java b/src/main/java/org/innercore/internal/serialization/JsonIO.java new file mode 100644 index 000000000..9c2020e4c --- /dev/null +++ b/src/main/java/org/innercore/internal/serialization/JsonIO.java @@ -0,0 +1,409 @@ +package org.innercore.internal.serialization; + +import org.innercore.icstd.serialization.*; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; + +public class JsonIO { + // WRITER + + private static boolean isScopeValue(Object o) { + return o instanceof JSONObject || o instanceof JSONArray || o == null || o == JSONObject.NULL; + } + + private static boolean isNonEmptyScopeValue(Object o) { + return o instanceof JSONObject || o instanceof JSONArray; + } + + private static float sanitizeFloat(float value) { + if (!Float.isFinite(value)) { + value = 0; + } + return value; + } + + private static double sanitizeDouble(double value) { + if (!Double.isFinite(value)) { + value = 0; + } + return value; + } + + private static Object validateJsonValue(Object value) { + if (value instanceof CharSequence) { + value = value.toString(); + } else if (value instanceof Number) { + if (!(value instanceof Long) && !Double.isFinite(((Number) value).doubleValue())) + value = 0.f; + } else if (!(value instanceof Boolean)) { + throw new IllegalArgumentException("passed invalid non-trivial value to json writer: " + value); + } + return value; + } + + public static class ScopedWriter extends BaseIO.ScopedWriter { + public ScopedWriter() { + super(); + } + + public ScopedWriter(BaseIO.BaseWriter parent) { + super(parent); + } + + public Object getResult() { + return resultObj; + } + + @Override + protected BaseIO.BaseWriter newObjectWriter(BaseIO.ScopedWriter parent) { + return new ObjectWriter(parent); + } + + @Override + protected BaseIO.BaseWriter newArrayWriter(BaseIO.ScopedWriter parent) { + return new ArrayWriter(parent); + } + + @Override + protected BaseIO.ScopedWriter newScopedWriter(BaseIO.BaseWriter parent) { + return new ScopedWriter(parent); + } + } + + public static class ObjectWriter extends BaseIO.BaseWriter implements IObjectWriter { + private String childScopeKey; + private final HashMap data = new HashMap<>(); + + ObjectWriter(BaseIO.ScopedWriter scope) { super(scope); } + + private IObjectWriter putAnyImpl(String key, Object value) { + if (childScope != null) throw new IllegalStateException("in child scope"); + else if (scope == null) throw new IllegalStateException("scope is closed"); + data.put(key, value); + return this; + } + + @Override public IObjectWriter putInt(String key, int value) { return putAnyImpl(key, value); } + @Override public IObjectWriter putLong(String key, long value) { return putAnyImpl(key, value); } + @Override public IObjectWriter putShort(String key, short value) { return putAnyImpl(key, value); } + @Override public IObjectWriter putByte(String key, byte value) { return putAnyImpl(key, value); } + @Override public IObjectWriter putFloat(String key, float value) { return putAnyImpl(key, sanitizeFloat(value)); } + @Override public IObjectWriter putDouble(String key, double value) { return putAnyImpl(key, sanitizeDouble(value)); } + @Override public IObjectWriter putBoolean(String key, boolean value) { return putAnyImpl(key, value); } + @Override public IObjectWriter putString(String key, String value) { return putAnyImpl(key, value); } + + @Override public boolean isPutAnySupported() { return true; } + @Override public void putAny(String key, Object value) { putAnyImpl(key, validateJsonValue(value)); } + + @Override + public IScopedWriter beginScope(String key) { + Object o = data.get(key); + if (isScopeValue(o) && data.containsKey(key)) throw new IllegalStateException("child scope '" + key + "' was already closed"); + IScopedWriter writer = beginScopeImpl(); + childScopeKey = key; + return writer; + } + + @Override + protected Object endThisScope() { + return new JSONObject(data); + } + + @Override + protected void writeEndedChildScope(Object childData) { + data.put(childScopeKey, childData); + } + } + + public static class ArrayWriter extends BaseIO.BaseWriter implements IArrayWriter { + private int childScopeIdx = 0; + private final ArrayList data = new ArrayList<>(); + + ArrayWriter(BaseIO.ScopedWriter scope) { super(scope); } + + @Override + public int size() { + return data.size(); + } + + private IArrayWriter addAnyImpl(Object value) { + validateStateForWrite(); + data.add(validateJsonValue(value)); + return this; + } + + private IArrayWriter addAnyImpl(int at, Object value) { + validateStateForWrite(); + data.add(at, validateJsonValue(value)); + return this; + } + + private IArrayWriter setAnyImpl(int idx, Object value) { + validateStateForWrite(); + Object o = data.get(idx); + if (o instanceof JSONObject || o instanceof JSONArray || o == null) { + throw new IllegalStateException("array writer already has child scope at idx " + idx + " it can't be overridden with a value"); + } + data.set(idx, validateJsonValue(value)); + return this; + } + + @Override public IArrayWriter addInt(int value) { return addAnyImpl(value); } + @Override public IArrayWriter addInt(int idx, int value) { return addAnyImpl(value); } + @Override public IArrayWriter setInt(int idx, int value) { return setAnyImpl(idx, value); } + @Override public IArrayWriter addLong(long value) { return addAnyImpl(value); } + @Override public IArrayWriter addLong(int idx, long value) { return addAnyImpl(idx, value); } + @Override public IArrayWriter setLong(int idx, long value) { return setAnyImpl(idx, value); } + @Override public IArrayWriter addShort(short value) { return addAnyImpl(value); } + @Override public IArrayWriter addShort(int idx, short value) { return addAnyImpl(idx, value); } + @Override public IArrayWriter setShort(int idx, short value) { return setAnyImpl(idx, value); } + @Override public IArrayWriter addByte(byte value) { return addAnyImpl(value); } + @Override public IArrayWriter addByte(int idx, byte value) { return addAnyImpl(idx, value); } + @Override public IArrayWriter setByte(int idx, byte value) { return setAnyImpl(idx, value); } + @Override public IArrayWriter addFloat(float value) { return addAnyImpl(value); } + @Override public IArrayWriter addFloat(int idx, float value) { return addAnyImpl(idx, sanitizeFloat(value)); } + @Override public IArrayWriter setFloat(int idx, float value) { return setAnyImpl(idx, sanitizeFloat(value)); } + @Override public IArrayWriter addDouble(double value) { return addAnyImpl(value); } + @Override public IArrayWriter addDouble(int idx, double value) { return addAnyImpl(idx, sanitizeDouble(value)); } + @Override public IArrayWriter setDouble(int idx, double value) { return setAnyImpl(idx, sanitizeDouble(value)); } + @Override public IArrayWriter addBoolean(boolean value) { return addAnyImpl(value); } + @Override public IArrayWriter addBoolean(int idx, boolean value) { return addAnyImpl(idx, value); } + @Override public IArrayWriter setBoolean(int idx, boolean value) { return setAnyImpl(idx, value); } + @Override public IArrayWriter addString(String value) { return addAnyImpl(value); } + @Override public IArrayWriter addString(int idx, String value) { return addAnyImpl(idx, value); } + @Override public IArrayWriter setString(int idx, String value) { return setAnyImpl(idx, value); } + + @Override public boolean isPutAnySupported() { return true; } + @Override public void addAny(Object value) { addAnyImpl(validateJsonValue(value)); } + @Override public void addAny(int idx, Object value) { addAnyImpl(idx, validateJsonValue(value)); } + @Override public void setAny(int idx, Object value) { setAnyImpl(idx, validateJsonValue(value)); } + + @Override + public IScopedWriter beginScope() { + return beginScope(size()); + } + + @Override + public IScopedWriter beginScope(int idx) { + IScopedWriter writer = beginScopeImpl(); + childScopeIdx = idx; + return writer; + } + + @Override + protected void writeEndedChildScope(Object childData) { + data.add(childScopeIdx, childData); + } + + @Override + protected Object endThisScope() { + return new JSONArray(data); + } + } + + // READER + + public static class ScopedReader extends BaseIO.ScopedReader { + public ScopedReader(BaseIO.BaseReader parent, Object data) { + super(parent, data == JSONObject.NULL ? null : data); + if (!isScopeValue(data)) { + throw new IllegalArgumentException("data must be a JSONArray or JSONObject or null or JSONOOject.NULL got " + data.getClass()); + } + } + + public ScopedReader(Object data) { + super(data == JSONObject.NULL ? null : data); + if (!isScopeValue(data)) { + throw new IllegalArgumentException("data must be a JSONArray or JSONObject or null or JSONOOject.NULL got " + data.getClass()); + } + } + + @Override + protected BaseIO.BaseReader newObjectReader(BaseIO.ScopedReader parent, Object data) { + return new ObjectReader(parent, data); + } + + @Override + protected BaseIO.BaseReader newArrayReader(BaseIO.ScopedReader parent, Object data) { + return new ArrayReader(parent, data); + } + + @Override + protected BaseIO.ScopedReader newScopedReader(BaseIO.BaseReader parent, Object data) { + return new ScopedReader(parent, data); + } + + @Override public boolean isObject() { + validateInScope(); + return data instanceof JSONObject; + } + + @Override + public boolean isArray() { + validateInScope(); + return data instanceof JSONArray; + } + + @Override + public boolean isEmpty() { + validateInScope(); + return data == null; + } + } + + public static class ObjectReader extends BaseIO.BaseReader implements IObjectReader { + private final JSONObject data; + ObjectReader(BaseIO.ScopedReader parent, Object data) { super(parent); this.data = (JSONObject) data; } + + @Override public boolean hasKey(String key) { return data.has(key); } + + @Override public ESerializedType getValueType(String key) { + validateStateForRead(); + Object value = data.opt(key); + if (value == null && !data.has(key)) + return ESerializedType.INVALID; + return BaseIO.getSerializedTypeFromObject(value); + } + + @Override public int getInt(String key, int fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof Number ? ((Number) o).intValue() : fallback; + } + @Override public long getLong(String key, long fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof Number ? ((Number) o).longValue() : fallback; + } + @Override public short getShort(String key, short fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof Number ? ((Number) o).shortValue() : fallback; + } + @Override public byte getByte(String key, byte fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof Number ? ((Number) o).byteValue() : fallback; + } + @Override public float getFloat(String key, float fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof Number ? ((Number) o).floatValue() : fallback; + } + @Override public double getDouble(String key, double fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof Number ? ((Number) o).doubleValue() : fallback; + } + @Override public boolean getBoolean(String key, boolean fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof Boolean ? (Boolean) o : fallback; + } + @Override public String getString(String key, String fallback) { + validateStateForRead(); + Object o = data.opt(key); + return o instanceof String ? (String) o : fallback; + } + + @Override public boolean isGetAnySupported() { return true; } + @Override public Object getAny(String key, Object fallback) { + validateStateForRead(); + Object o = data.opt(key); + return isScopeValue(o) ? fallback : o; + } + + @Override public Iterable iterKeys() { return data::keys; } + + @Override + public IScopedReader beginScope(String key) { + validateStateForRead(); + Object o = data.opt(key); + if (isScopeValue(o)) { + return beginScopeImpl(o); + } else { + return beginScopeImpl(null); + } + } + } + + public static class ArrayReader extends BaseIO.BaseReader implements IArrayReader { + private final JSONArray data; + ArrayReader(BaseIO.ScopedReader parent, Object data) { super(parent); this.data = (JSONArray) data; } + + @Override public int size() { return data.length(); } + + private void validateRead(int idx) { + validateStateForRead(); + if (idx < 0 || idx >= data.length()) throw new IndexOutOfBoundsException("index out of range: " + idx + " len=" + data.length()); + } + + @Override + public ESerializedType getValueType(int idx) { + validateRead(idx); + return BaseIO.getSerializedTypeFromObject(data.opt(idx)); + } + + @Override public int getInt(int idx, int fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof Number ? ((Number) o).intValue() : fallback; + } + @Override public long getLong(int idx, long fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof Number ? ((Number) o).longValue() : fallback; + } + @Override public short getShort(int idx, short fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof Number ? ((Number) o).shortValue() : fallback; + } + @Override public byte getByte(int idx, byte fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof Number ? ((Number) o).byteValue() : fallback; + } + @Override public float getFloat(int idx, float fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof Number ? ((Number) o).floatValue() : fallback; + } + @Override public double getDouble(int idx, double fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof Number ? ((Number) o).doubleValue() : fallback; + } + @Override public boolean getBoolean(int idx, boolean fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof Boolean ? (Boolean) o : fallback; + } + @Override public String getString(int idx, String fallback) { + validateRead(idx); + Object o = data.opt(idx); + return o instanceof String ? (String) o : fallback; + } + + @Override public boolean isGetAnySupported() { return true; } + @Override public Object getAny(int idx, Object fallback) { + validateRead(idx); + Object o = data.opt(idx); + return isScopeValue(o) ? fallback : o; + } + + @Override + public IScopedReader beginScope(int idx) { + validateRead(idx); + Object o = data.opt(idx); + if (isScopeValue(o)) { + return beginScopeImpl(o); + } else { + return beginScopeImpl(null); + } + } + } +} diff --git a/src/main/resources/innercore/coreengine/core-engine.dev.js b/src/main/resources/innercore/coreengine/core-engine.dev.js index 452c7e03d..fd02fc5b2 100755 --- a/src/main/resources/innercore/coreengine/core-engine.dev.js +++ b/src/main/resources/innercore/coreengine/core-engine.dev.js @@ -1,6 +1,6 @@ // noinspection JSUnresolvedVariable,JSUnresolvedFunction,EqualityComparisonWithCoercionJS,JSUnusedGlobalSymbols,JSUnusedLocalSymbols -var CORE_ENGINE_VERSION = "2.1"; +var CORE_ENGINE_VERSION = "2.0"; var CORE_ENGINE_API_LEVEL = 12; var CORE_ENGINE_CONFIG_LOCK = 4; @@ -26,7 +26,6 @@ function runVersionDependentDataScript(name) { } } - var Network = MCSystem.getNetwork(); var EntityManager = ECS.getEntityManager(); var ECS_INVALID_ENTITY = ECS.getInvalidEntity(); @@ -514,6 +513,12 @@ var GameAPI = { getEngineVersion: function() { return CORE_ENGINE_VERSION; }, + getPackVersion: function() { + return MCSystem.getPackVersion(); + }, + getPackVersionCode: function() { + return MCSystem.getPackVersionCode(); + }, spendItemsInCreative: false, isDeveloperMode: false, isItemSpendingAllowed: function(player) { @@ -526,11 +531,9 @@ var GameAPI = { }, simulateBackPressed: function() { MCSystem.simulateBackPressed(); - }, - addWorldToCache() {},//client only, - updateWorlds() {}, // client only, - getWorldsCount() {} // client only, + } }; +injectBindingsIntoScope("Game", GameAPI, false); Callback.addCallback("CoreConfigured", function(config) { GameAPI.isDeveloperMode = GameAPI.spendItemsInCreative = config.getBool("developer_mode"); @@ -691,13 +694,12 @@ var TileEntityBasePrototype = { defaultValues: {}, _runInit: function() { Saver.registerObject(this, this.saverId); - this.blockSource = BlockSource.getDefaultForDimension(this.dimension); - + this.blockSource = this.blockSource || BlockSource.getDefaultForDimension(this.dimension); if (!this.blockSource) { this.isLoaded = false; return false; } - if (!TileEntity.isTileEntityLoaded(this)) { + if (!this._objHandle.isLoaded()) { this.isLoaded = false; return false; } @@ -727,11 +729,27 @@ var TileEntityBasePrototype = { self.onDisconnectionPlayer && this.networkEntity.setDisconnectionPlayerListener(function(client){ self.onDisconnectionPlayer(client); }) + if (this.__delayedRedstone) { + for (let i in this.__delayedRedstone) { + this.redstone(this.__delayedRedstone[i]) + } + this.__delayedRedstone = null; + } this.__initialized = true; this.noupdate = !this.tick; return true; }, - update: function() { + _loadImpl: function() { + if (!this.__initialized) { + if (!this._runInit()) { + this.noupdate = true; + return; + } + } else { + this.load(); + } + }, + update: function(dt) { if (this.isLoaded) { if (!this.__initialized) { if (!this._runInit()) { @@ -739,14 +757,59 @@ var TileEntityBasePrototype = { return; } } - if (this.tick) { - this.tick(); - } + this.tick && this.tick(dt); } }, _to_string: function() { return "TileEntity{" + this.networkEntityTypeName + " pos=(" + this.x + ", " + this.y + ", " + this.z + "), dimension=" + this.dimension + "}"; }, + _createdImpl: function() { + let pos = this._objHandle.getBlockPos(); + this.x = pos.x; + this.y = pos.y; + this.z = pos.z; + this.dimension = this.blockSource.getDimension(); + this.created(); + }, + _readImpl: function(obj) { + if (obj.data) for (let property in obj.data) { + this.data[property] = obj.data[property]; + } + // container + if (obj.container) this.container = obj.container; + if (!this.container) { + this.container = this.useNetworkItemContainer ? new ItemContainer() : new UI.Container(); + } + if (this.container.isLegacyContainer()) { + if (this.useNetworkItemContainer) { + // noinspection JSCheckFunctionSignatures + print("[" + entityTypeName + "] upgrading legacy container") + this.container = new ItemContainer(this.container); + } + } else { + if (!this.useNetworkItemContainer) { + // noinspection JSCheckFunctionSignatures + print("[" + entityTypeName + "] downgrading to legacy container") + this.container = this.container.asLegacyContainer(); + } + } + this.container.setParent(this); + // liquid + if (obj.liquids) this.liquidStorage = obj.liquids; + if (!this.liquidStorage) { + this.liquidStorage = new LiquidRegistry.Storage(); + } + this.liquidStorage.setParent(this); + // TODO: don't know why the fuck it is even needed but whatever + try { + this.setParent(this); + } catch (e) {} + // register actual saver + Saver.registerObject(this, this.saverId); + }, + _writeImpl: function() { + return { data: this.data, container: this.container, liquids: this.liquidStorage }; + }, created: function() {}, load: function() {}, unload: function() {}, @@ -761,6 +824,22 @@ var TileEntityBasePrototype = { destroy: function() { return false; }, + _destroyImpl: function(ctx) { + if (this.destroy(false, false, ctx)) { + print("preventing tile entity destruction is no longer allowed") + } + if (this.networkEntity) { + this.networkEntity.remove(); + this.networkEntity = null; + } + this.container.close(); + if (this.container.isLegacyContainer()) { + this.container.dropAt(this.x + 0.5, this.y + 0.5, this.z + 0.5); + } else { + this.container.dropAt(this.blockSource, this.x + 0.5, this.y + 0.5, this.z + 0.5); + this.container.removeEntity(); + } + }, getGuiScreen: function() { return null; }, @@ -789,8 +868,13 @@ var TileEntityBasePrototype = { return true; } } + } else { + var screen = this.getGuiScreen(); + if (screen) { + this.container.openAs(screen); + return true; + } } - return false; }, selfDestroy: function() { @@ -817,6 +901,15 @@ var TileEntity = { this.tileEntityCacheMap = {}; }, registerPrototype: function(blockID, customPrototype) { + if (!blockID) { + Logger.info("ERROR", "TileEntity cannot be registered with invalid numeric ID: " + blockID); + return false; + } + if (this.tileEntityPrototypes[blockID] != null) { + Logger.info("ERROR", "TileEntity with ID " + IDRegistry.getNameByID(blockID) + " (id=" + blockID + ") was already occupied"); + return false; + } + var Prototype = {}; for (let property in TileEntityBasePrototype) { Prototype[property] = TileEntityBasePrototype[property]; @@ -827,67 +920,29 @@ var TileEntity = { Prototype.blockID = blockID; this.tileEntityPrototypes[blockID] = Prototype; - var saverName = "_TILE_ENTITY" + blockID; + Prototype.saverId = "b_ent:s-" + IDRegistry.getNameByID(blockID); var entityTypeName = "TE-" + IDRegistry.getNameByID(blockID); - Prototype.saverId = Saver.registerObjectSaver(saverName, { + var legacySaverId = Saver.registerObjectSaver("_TILE_ENTITY" + blockID, { read: function(obj) { if (!obj || !obj.coords) { - print("Corrupted save "+JSON.stringify(obj)); - return; - } - - if(TileEntity.getTileEntity(obj.coords.x, obj.coords.y, obj.coords.z, BlockSource.getDefaultForDimension(obj.coords.d))){ - print("Duplicate TileEntity detected: "+obj.coords.x+":"+obj.coords.y+":"+obj.coords.z); return; } - var instance = {}; for (var property in Prototype) { instance[property] = Prototype[property]; } - instance.data = obj.data; - if(!instance.data){ - instance.data = {}; - for (let property in Prototype.defaultValues) { - instance.data[property] = Prototype.defaultValues[property]; - } - print("Corrupted save data "+JSON.stringify(obj)); - } instance.x = obj.coords.x || 0; instance.y = obj.coords.y || 0; instance.z = obj.coords.z || 0; instance.dimension = obj.coords.d || 0; instance.container = obj.container; instance.networkData = new SyncedNetworkData(); - instance.liquidStorage = obj.liquidStorage; - if (!instance.container) { - instance.container = Prototype.useNetworkItemContainer ? new ItemContainer() : new UI.Container(); - } - if (instance.container.isLegacyContainer()) { - if (Prototype.useNetworkItemContainer) { - // noinspection JSCheckFunctionSignatures - print("[" + entityTypeName + "] upgrading legacy container") - instance.container = new ItemContainer(instance.container); - } - } else { - if (!Prototype.useNetworkItemContainer) { - // noinspection JSCheckFunctionSignatures - print("[" + entityTypeName + "] downgrading to legacy container") - instance.container = instance.container.asLegacyContainer(); - } - } - - try { - instance.container.setParent(instance); - } catch (e) {} - - if (!instance.liquidStorage) { - instance.liquidStorage = new LiquidRegistry.Storage(); - } - instance.liquidStorage.setParent(instance); - TileEntity.addUpdatableAsTileEntity(instance); - return instance; + instance.data = obj.data; + obj.liquids = obj.liquidStorage; // rename for _readImpl + instance._readImpl(obj); + BlockEntityRegistry.addExistingScriptBlockEntityToServerScene(entityTypeName, instance, instance.dimension, instance.x, instance.y, instance.z); + return null; // don't add it }, save: function(obj) { return { @@ -913,7 +968,6 @@ var TileEntity = { // create client tile entity from Prototype.client let networkData = SyncedNetworkData.getClientSyncedData(packet.sd); - networkData.fromJSON(packet.data); var client = { x: packet.x, y: packet.y, @@ -925,16 +979,17 @@ var TileEntity = { __initialized: false, noupdate: false, remove: false, - update: function() { + update: function(dt) { if (!this.__initialized) { this.__initialized = true; this.load(); + networkData.fromJSON(packet.data); this.remove = this.remove || !this.tick; if (this.remove) { return; } } - this.tick(); + this.tick(dt); }, load: function() {}, @@ -1012,6 +1067,18 @@ var TileEntity = { if (customPrototype.redstone) { Block.setRedstoneTile(blockID, -1, true); } + + BlockEntityRegistry.registerType(blockID, entityTypeName, Prototype.saverId, Prototype.networkEntityType, function() { + let tileEntity = TileEntity.createTileEntityForPrototype(Prototype, false); + if (tileEntity.saverId) { + Saver.registerObject(tileEntity, tileEntity.saverId); + } + tileEntity.remove = false; + tileEntity.isLoaded = false; + return tileEntity; + }, true); + + return true; }, getPrototype: function(blockID) { return this.tileEntityPrototypes[blockID]; @@ -1044,173 +1111,86 @@ var TileEntity = { return tileEntity; }, addTileEntity: function(x, y, z, blockSource) { + if (!blockSource) blockSource = BlockSource.getDefaultForDimension(Player.getDimension()); if (this.getTileEntity(x, y, z, blockSource)) { return null; } - var tile = blockSource ? blockSource.getBlockId(x, y, z) : getTile(x, y, z); - var Prototype = this.getPrototype(tile); - if (Prototype) { - var tileEntity = this.createTileEntityForPrototype(Prototype, true); - tileEntity.x = x; - tileEntity.y = y; - tileEntity.z = z; - tileEntity.dimension = blockSource ? blockSource.getDimension() : Player.getDimension(); - this.tileEntityList.push(tileEntity); - this.tileEntityCacheMap[x + "," + y + "," + z + "," + tileEntity.dimension] = tileEntity; - tileEntity.created(); - Callback.invokeCallback("TileEntityAdded", tileEntity, true); - return tileEntity; - } - return null; + return blockSource.getOrAddBlockEntity(x, y, z); }, addUpdatableAsTileEntity: function(updatable) { - updatable.remove = false; - updatable.isLoaded = true; - if (updatable.saverId && updatable.saverId !== -1) { - Saver.registerObject(updatable, updatable.saverId); - } - this.tileEntityList.push(updatable); - this.tileEntityCacheMap[updatable.x + "," + updatable.y + "," + updatable.z + "," + updatable.dimension] = updatable; - Callback.invokeCallback("TileEntityAdded", updatable, false); }, getTileEntity: function(x, y, z, blockSource) { - var key = x + "," + y + "," + z; - if (blockSource) { - key += "," + blockSource.getDimension(); - } else { - key += "," + Player.getDimension(); - } - var tileEntity = this.tileEntityCacheMap[key]; - if (tileEntity) { - if (tileEntity.x === x && tileEntity.y === y && tileEntity.z === z) { - return tileEntity; - } else { - delete this.tileEntityCacheMap[key]; - } - } - return null; + if (!blockSource) blockSource = BlockSource.getDefaultForDimension(Player.getDimension()); + return blockSource.getBlockEntityNotNative(x, y, z); }, - destroyTileEntity: function(tileEntity, fromDestroyBlock, isDropAllowed) { - try{ - if (tileEntity.destroy(fromDestroyBlock || false, isDropAllowed || false)) { - return false; - } - }catch(e){ - print("Error destroy tile "+e); - } - tileEntity.update = function() {}; // make sure tile entity will not tick anymore, because it is already cleaned up - if (tileEntity.networkEntity) { - tileEntity.networkEntity.remove(); - tileEntity.networkEntity = null; - } - tileEntity.remove = true; - tileEntity.noupdate = false; - tileEntity.container.close(); - if (tileEntity.container.isLegacyContainer()) { - tileEntity.container.dropAt(tileEntity.x + 0.5, tileEntity.y + 0.5, tileEntity.z + 0.5); - } else if(tileEntity.blockSource) { - tileEntity.container.dropAt(tileEntity.blockSource, tileEntity.x + 0.5, tileEntity.y + 0.5, tileEntity.z + 0.5); - tileEntity.container.removeEntity(); - } - for (var i in this.tileEntityList) { - if (this.tileEntityList[i] === tileEntity) { - this.tileEntityList.splice(i--, 1); - } - } - delete this.tileEntityCacheMap[tileEntity.x + "," + tileEntity.y + "," + tileEntity.z + "," + tileEntity.dimension]; - Callback.invokeCallback("TileEntityRemoved", tileEntity); + destroyTileEntity: function(tileEntity, _fromDestroyBlock, _isDropAllowed) { + if (!tileEntity || !tileEntity._objHandle) + return false; + tileEntity._objHandle.destroyNow(); return true; }, - destroyTileEntityAtCoords: function(x, y, z, blockSource, isDropAllowed) { + destroyTileEntityAtCoords: function(x, y, z, blockSource, _isDropAllowed) { var tileEntity = this.getTileEntity(x, y, z, blockSource); if (tileEntity) { - return this.destroyTileEntity(tileEntity, false, isDropAllowed); + return this.destroyTileEntity(tileEntity, false, _isDropAllowed); } return false; }, isTileEntityLoaded: function(tileEntity) { - /* - this will check not only current chunk, but 5 chunks around, to exclude processing tile entities in lazy chunks - checked chunks map: - + + - # - + + - */ var blockSource = tileEntity.blockSource || BlockSource.getDefaultForDimension(tileEntity.dimension); if (!blockSource) { return false; } - var chunkX = Math.floor(tileEntity.x / 16); - var chunkZ = Math.floor(tileEntity.z / 16); - return blockSource.isChunkLoaded(chunkX, chunkZ) && - blockSource.isChunkLoaded(chunkX + 1, chunkZ + 1) && - blockSource.isChunkLoaded(chunkX + 1, chunkZ - 1) && - blockSource.isChunkLoaded(chunkX - 1, chunkZ + 1) && - blockSource.isChunkLoaded(chunkX - 1, chunkZ - 1); + return tileEntity.isLoaded; }, checkTileEntityForIndex: function(index) { - var tileEntity = this.tileEntityList[index]; - var wasLoaded = tileEntity.isLoaded; - tileEntity.isLoaded = this.isTileEntityLoaded(tileEntity); - if (tileEntity.__initialized) { - if (!wasLoaded && tileEntity.isLoaded) { - tileEntity.load(); - } - if (wasLoaded && !tileEntity.isLoaded) { - tileEntity.unload(); - } - tileEntity.noupdate = !tileEntity.tick || !tileEntity.isLoaded; // do not tick tile entities, that are not loaded, and dont have tick function - } else if (tileEntity.isLoaded) { - tileEntity.noupdate = false; // tile entity must update at least once to initialize - } - tileEntity.onCheckerTick(tileEntity.__initialized, tileEntity.isLoaded, wasLoaded); - if (tileEntity.isLoaded && tileEntity.blockSource) { - var isPlaced = tileEntity.blockSource.getBlockId(tileEntity.x, tileEntity.y, tileEntity.z) === tileEntity.blockID; - if (!isPlaced) { - this.DeployDestroyChecker(tileEntity); - } - } }, tileEntityCheckIndex: 0, - CheckTileEntities: function() { - if (this.tileEntityList.length > 0) { - for (var i = 0; i < this.tileEntityList.length / 128; i++) { - this.tileEntityCheckIndex = (this.tileEntityCheckIndex + 1) % this.tileEntityList.length; - this.checkTileEntityForIndex(this.tileEntityCheckIndex); - } - } - }, - DeployDestroyChecker: function(tileEntity) { - if (tileEntity.__checkInProgress) { + CheckTileEntities: function() {}, + DeployDestroyChecker: function(tileEntity) {} +}; + +Callback.addCallback("TileEntityAdded", function(te) { + let list = TileEntity.tileEntityList; + te.__teListPos = list.length; + list.push(te); +}); + +Callback.addCallback("TileEntityRemoved", function(te) { + let list = TileEntity.tileEntityList; + if (list[te.__teListPos] == te) { + let last = list[list.length - 1]; + last.__teListPos = te.__teListPos; + list[te.__teListPos] = last; + list.pop() + te.__teListPos = -1; + return; + } + print("tile entity list integrity failed " + te + " idx=" + te.__teListPos); + te.__teListPos = -1; + for (let i = 0, e = list.length; i < e; i++) { + if (list[i] == te) { + let last = list[list.length - 1]; + last.__teListPos = i; + list[i] = last; + list.pop() return; } - tileEntity.__checkInProgress = true; - var checker = { - tileEntity: tileEntity, - age: 0, - update: function() { - var isPlaced = tileEntity.blockSource.getBlockId(this.tileEntity.x, this.tileEntity.y, this.tileEntity.z) === tileEntity.blockID; - if (isPlaced) { - this.tileEntity.__checkInProgress = false; - this.remove = true; - return; - } - if (this.age++ > TILE_ENTITY_CHECKER_ITERATIONS) { - TileEntity.destroyTileEntity(this.tileEntity, false); - this.tileEntity.__checkInProgress = false; - this.remove = true; - } - } - }; - Updatable.addUpdatable(checker); } -}; -Callback.addCallback("tick", function() { - TileEntity.CheckTileEntities(); + print("failed to remove tile entity from list " + te + " idx=" + te.__teListPos); }); + Callback.addCallback("RedstoneSignal", function(coords, params, onLoad, region) { var tileEntity = TileEntity.getTileEntity(coords.x, coords.y, coords.z, region); - tileEntity && tileEntity.__initialized && tileEntity.redstone(params); + if (tileEntity) { + if (tileEntity.__initialized) { + tileEntity.redstone(params); + } else { + if (!tileEntity.__delayedRedstone) + tileEntity.__delayedRedstone = [] + tileEntity.__delayedRedstone.push(params); + } + } }); Callback.addCallback("BreakBlock", function(blockSource, coords, fullTile, isDropAllowed, player, item) { if (GameAPI.isActionPrevented()) { @@ -1590,11 +1570,11 @@ var BlockRegistry = { var bucketNameId = bucketData.id || (nameID + "_bucket"); IDRegistry.genItemID(bucketNameId); var idBucket = ItemID[bucketNameId]; - ItemRegistry.createItem(bucketNameId, bucketData.name || "Bucket Of " + blockName, bucketData.texture || ["missing", 0], { stack: 1, isTech: bucketData.isTech }); - ItemRegistry.setLiquidClip(idBucket, true); + ItemRegistry.createItem(bucketNameId, bucketData.name || "Bucket Of " + blockName, bucketData.texture || {name:"missing"}, { stack: 1, isTech: bucketData.isTech }); + Item.setLiquidClip(idBucket, true); var emptyId = bucketData.emptyId || 325; if (emptyId !== 325) - ItemRegistry.setLiquidClip(emptyId, true); + Item.setLiquidClip(emptyId, true); LiquidRegistry.registerItem(liquidRegistryId, { id: emptyId, data: 0 }, { id: idBucket, data: 0 }); var canReplaceWithLiquid = function(block) { return block.id === idStill || block.id === idFlowing || canTileBeReplaced(block.id, block.data); @@ -1617,7 +1597,7 @@ var BlockRegistry = { EntityAPI.setCarriedItem(player, emptyId, 1, 0, null); }) } - WorldAPI.playSoundAtEntity(player, bucketData.fillSound || "bucket.fill_water", 1, 1); + blockSource.playSoundAtEntity(player, bucketData.fillSound || "bucket.fill_water"); }); var clickFunc = function(coords, item, block, player) { if (block.data === 0 && item.id === emptyId && item.data === 0) { @@ -1634,7 +1614,7 @@ var BlockRegistry = { EntityAPI.setCarriedItem(player, emptyId, item.count - 1, 0, null); } } - WorldAPI.playSoundAtEntity(player, bucketData.emptySound || "bucket.empty_water", 1, 1); + blockSource.playSoundAtEntity(player, bucketData.emptySound || "bucket.empty_water"); } }; BlockRegistry.registerClickFunction(nameIdStill, clickFunc); @@ -1649,11 +1629,11 @@ var BlockRegistry = { GameAPI.prevent(); region.setBlock(coords.relative.x, coords.relative.y, coords.relative.z, idFlowing, 0); dispenser.setSlot(slot, emptyId, 1, 0, null); - WorldAPI.playSound(coords.relative.x + .5, coords.relative.y + .5, coords.relative.z + .5, bucketData.emptySound || "bucket.empty_water", 1, 1); + region.playSound(coords.relative.x + .5, coords.relative.y + .5, coords.relative.z + .5, bucketData.emptySound || "bucket.empty_water"); } }); - // vanilla bucket is patched in such way, that it will call this callback only for custom liquids in front of dispenser + // vanilla bucket is patched in such way, that it will call this callback only for custom liquids in front of Callback.addCallback("ItemDispensed", function(coords, item, region, slot) { if (item.data !== 0) return; var dispenser = region.getBlockEntity(coords.x, coords.y, coords.z); @@ -1664,7 +1644,7 @@ var BlockRegistry = { if (liquid && block.data === 0) { GameAPI.prevent(); region.setBlock(coords.relative.x, coords.relative.y, coords.relative.z, 0, 0); - WorldAPI.playSound(coords.relative.x + .5, coords.relative.y + .5, coords.relative.z + .5, bucketData.emptySound || "bucket.empty_water", 1); + region.playSound(coords.relative.x + .5, coords.relative.y + .5, coords.relative.z + .5, bucketData.emptySound || "bucket.empty_water"); dispenser.setSlot(slot, item.id, item.count - 1, item.data, item.extra); for (var i = 0; i < 9; i++) { @@ -1704,23 +1684,28 @@ var BlockRegistry = { } this.createBlock(namedID, rotatedBlocks, blockType); this.registerPlaceFunction(namedID, function(coords, item, block, player, blockSource) { - var yaw = Math.floor((Entity.getYaw(player) - 45) / 90); + let yaw = Math.floor((Entity.getYaw(player) - 45) / 90); while (yaw < 0) { yaw += 4; } while (yaw > 3) { yaw -= 4; } - var meta = { + let meta = { 0: 2, 1: 0, 2: 3, 3: 1 } [yaw]; - if (canTileBeReplaced(blockSource.getBlockId(coords.relative.x, coords.relative.y, coords.relative.z))) { + if (canTileBeReplaced(block.id, block.data)) { + blockSource.setBlock(coords.x, coords.y, coords.z, item.id, Math.floor(item.data / 4) * 4 + meta); + return coords; + } + let tile = blockSource.getBlock(coords.relative.x, coords.relative.y, coords.relative.z); + if (canTileBeReplaced(tile.id, tile.data)) { blockSource.setBlock(coords.relative.x, coords.relative.y, coords.relative.z, item.id, Math.floor(item.data / 4) * 4 + meta); + return coords.relative; } - return coords.relative; }); this.registerDropFunction(namedID, function(coords, blockID, blockData) { return [ @@ -2025,6 +2010,20 @@ var BlockRegistry = { getPlaceFunc: function(block) { return this.placeFuncs[block]; }, + inplaceFuncs: [], + registerInplaceFunctionForID: function(block, func) { + this.inplaceFuncs[block] = func; + }, + registerInplaceFunction: function(namedID, func) { + var numericID = this.getNumericId(namedID); + if (numericID == -1) { + return false; + } + this.registerInplaceFunctionForID(numericID, func); + }, + getInplaceFunc: function(block) { + return this.inplaceFuncs[block]; + }, setBlockShape: function(id, pos1, pos2, data) { Block.setShape(id, pos1.x, pos1.y, pos1.z, pos2.x, pos2.y, pos2.z, data); }, @@ -2135,36 +2134,36 @@ var ITEM_BASE_PROTOTYPE = { } var maxDamage = this.getMaxDamage(item); if (maxDamage != null) { - ItemRegistry.setMaxDamage(this.id, maxDamage); + Item.setMaxDamage(this.id, maxDamage); } var category = this.getCategory(item); if (category != null) { - ItemRegistry.setCategory(this.id, category); + Item.setCategory(this.id, category); } var enchant = this.getEnchant(item); if (enchant != null) { - ItemRegistry.setEnchantType(this.id, enchant.type, enchant.value); + Item.setEnchantType(this.id, enchant.type, enchant.value); } var useAnimation = this.getUseAnimation(item); if (useAnimation != null) { - ItemRegistry.setUseAnimation(this.id, useAnimation); + Item.setUseAnimation(this.id, useAnimation); } var maxUseDuration = this.getMaxUseDuration(item); if (maxUseDuration != null) { - ItemRegistry.setMaxUseDuration(this.id, maxUseDuration); + Item.setMaxUseDuration(this.id, maxUseDuration); } var properties = this.getProperties(item); if (properties) { properties.foil = this.isEnchanted(item); - ItemRegistry.setProperties(this.id, properties); + Item.setProperties(this.id, properties); } var toolRender = this.isToolRender(item); if (toolRender != null) { - ItemRegistry.setToolRender(this.id, toolRender); + Item.setToolRender(this.id, toolRender); } var stackByData = this.isStackedByData(item); if (stackByData != null) { - ItemRegistry.setStackedByData(this.id, stackByData); + Item.setStackedByData(this.id, stackByData); } var armorFunc = this.getArmorFuncs(item); if (armorFunc != null) { @@ -2234,37 +2233,20 @@ var ItemRegistry = { idSource: ItemID, useFunctions: {}, throwableFunctions: {}, - getNumericId: function(id) { - if (typeof(id) == "string") { - var _id = this.idSource[id]; - if (!_id) { - Logger.Log("Invalid item namedID: " + id + ", -1 will be returned", "ERROR"); - return -1; - } - id = _id; - } - return parseInt(id); - }, - getItemById: function(id) { - return Item.getItemById(this.getNumericId(id)); - }, createItem: function(namedID, name, texture, params) { if (!params) { params = {}; } params.stack = params.stack || 64; - var numericID = this.idSource[namedID]; - if (!numericID) { - Logger.Log("Invalid item namedID: " + namedID, "ERROR"); - return false; - } + var numericID = Item.getNumericId(namedID); + if (numericID == -1) return false; var item = Item.createItem(numericID, namedID, name, texture.name, texture.meta || texture.data || 0); - item.setMaxStackSize(params.stack); + Item.setMaxStackSize(numericID, params.stack); if (!params.isTech) { Player.addItemCreativeInv(numericID, 1, 0); } if (params.category !== undefined) { - Item.setCategoryForId(numericID, params.category); + Item.setCategory(numericID, params.category); } return item; }, @@ -2274,18 +2256,15 @@ var ItemRegistry = { } params.stack = params.stack || 64; params.food = params.food || 1; - var numericID = this.idSource[namedID]; - if (!numericID) { - Logger.Log("Invalid item namedID: " + namedID, "ERROR"); - return null; - } + var numericID = Item.getNumericId(namedID); + if (numericID == -1) return false; var item = Item.createFoodItem(numericID, namedID, name, texture.name, texture.meta || texture.data || 0, params.food); - item.setMaxStackSize(params.stack || 64); + Item.setMaxStackSize(numericID, params.stack || 64); if (!params.isTech) { Player.addItemCreativeInv(numericID, 1, 0); } if (params.category !== undefined) { - Item.setCategoryForId(numericID, params.category); + Item.setCategory(numericID, params.category); } return item; }, @@ -2320,18 +2299,15 @@ var ItemRegistry = { Logger.Log("Invalid armor type for item " + namedID + ": " + params.type + ",use: \"helmet\", \"chestplate\", \"leggings\", \"boots\"", "ERROR"); return; } - var numericID = this.idSource[namedID]; - if (!numericID) { - Logger.Log("Invalid item namedID: " + namedID, "ERROR"); - return false; - } + var numericID = Item.getNumericId(namedID); + if (numericID == -1) return false; var item = Item.createArmorItem(numericID, namedID, name, texture.name, texture.meta || texture.data || 0, params.texture, armorType, params.armor, params.durability, params.knockbackResist || 0); - item.setMaxStackSize(params.stack || 1); + Item.setMaxStackSize(numericID, params.stack || 1); if (!params.isTech) { Player.addItemCreativeInv(numericID, 1, 0); } if (params.category !== undefined) { - Item.setCategoryForId(numericID, params.category); + Item.setCategory(numericID, params.category); } return item; }, @@ -2340,13 +2316,10 @@ var ItemRegistry = { params = {}; } params.stack = params.stack || 64; - var numericID = this.idSource[namedID]; - if (!numericID) { - Logger.Log("Invalid item namedID: " + namedID, "ERROR"); - return false; - } + var numericID = Item.getNumericId(namedID); + if (numericID == -1) return false; var item = Item.createThrowableItem(numericID, namedID, name, texture.name, texture.meta || texture.data || 0); - item.setMaxStackSize(params.stack || 64); + Item.setMaxStackSize(numericID, params.stack || 64); if (!params.isTech) { Player.addItemCreativeInv(numericID, 1, 0); } @@ -2355,33 +2328,13 @@ var ItemRegistry = { isNativeItem: function(id) { return IDRegistry.isVanilla(id); }, - getMaxDamage: function(id) { - return Item.getMaxDamage(id); - }, - getMaxStack: function(id, data) { - return Item.getMaxStackSize(id, data); - }, - getName: function(id, data, extra) { - return Item.getName(id, data, extra); - }, - getArmorValue: function(id) { - return Item.getArmorValue(id); - }, - isValid: function(id, data) { - return Item.isValid(id); - }, addToCreative: function(id, count, data, extra) { - id = this.getNumericId(id); + id = Item.getNumericId(id); if (id == -1) { return; } Player.addItemCreativeInv(id, count, data, extra); }, - addCreativeGroup: function(name, displayedName, ids) { - for (var i in ids) { - Item.addToCreativeGroup(name, displayedName, ids[i]); - } - }, describeItem: function(numericID, description) { this.setCategory(numericID, description.category || 0); this.setToolRender(numericID, description.toolRender); @@ -2398,64 +2351,12 @@ var ItemRegistry = { this.setEnchantType(numericID, description.enchant.type, description.enchant.value); } }, - setCategory: function(id, category) { - Item.setCategoryForId(id, category); - }, - setEnchantType: function(id, enchant, value) { - this.getItemById(id).setEnchantType(enchant || 0, value || 0); - }, - setArmorDamageable: function(id, val) { - this.getItemById(id).setArmorDamageable(val); - }, - setShouldDespawn: function(id, val) { - this.getItemById(id).setShouldDespawn(val); - }, - setFireResistant: function(id, val) { - this.getItemById(id).setFireResistant(val); - }, - setExplodable: function(id, val) { - this.getItemById(id).setExplodable(val); - }, - - addRepairItemIds: function(id, items) { - var item = this.getItemById(id); - for (var i in items) { - item.addRepairItem(items[i]); - } - }, - setToolRender: function(id, enabled) { - this.getItemById(id).setHandEquipped(enabled); - }, - setMaxDamage: function(id, maxdamage) { - this.getItemById(id).setMaxDamage(maxdamage); - }, - setGlint: function(id, enabled) { - this.getItemById(id).setGlint(enabled); - }, - setLiquidClip: function(id, enabled) { - this.getItemById(id).setLiquidClip(enabled); - }, - setStackedByData: function(id, enabled) { - this.getItemById(id).setStackedByData(enabled); - }, - setAllowedInOffhand: function(id, allowed) { - this.getItemById(id).setAllowedInOffhand(allowed); - }, - setProperties: function(id, props) { - this.getItemById(id).setProperties(props); - }, - setUseAnimation: function(id, animType) { - this.getItemById(id).setUseAnimation(animType || 0); - }, - setMaxUseDuration: function(id, duration) { - this.getItemById(id).setMaxUseDuration(duration); - }, registerUseFunctionForID: function(numericID, useFunc) { this.useFunctions[numericID] = useFunc; return true; }, registerUseFunction: function(namedID, useFunc) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } @@ -2472,7 +2373,7 @@ var ItemRegistry = { return true; }, registerThrowableFunction: function(namedID, useFunc) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } @@ -2486,7 +2387,7 @@ var ItemRegistry = { }, iconOverrideFunctions: {}, registerIconOverrideFunction: function(namedID, func) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } @@ -2503,14 +2404,14 @@ var ItemRegistry = { } }, nameOverrideFunctions: {}, - setItemNameOverrideCallbackForced: requireMethodFromNativeAPI("api.NativeAPI", "setItemNameOverrideCallbackForced"), + setItemNameOverrideCallbackForced: Item.setNameOverrideCallbackForced, registerNameOverrideFunction: function(namedID, func, preventForcing) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } if (!preventForcing) { - this.setItemNameOverrideCallbackForced(numericID, true); + Item.setNameOverrideCallbackForced(numericID, true); } this.nameOverrideFunctions[numericID] = func; }, @@ -2525,7 +2426,7 @@ var ItemRegistry = { }, noTargetUseFunctions: {}, registerNoTargetUseFunction: function(namedID, func) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } @@ -2539,7 +2440,7 @@ var ItemRegistry = { }, usingReleasedFunctions: {}, registerUsingReleasedFunction: function(namedID, func) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } @@ -2553,7 +2454,7 @@ var ItemRegistry = { }, usingCompleteFunctions: {}, registerUsingCompleteFunction: function(namedID, func) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } @@ -2567,7 +2468,7 @@ var ItemRegistry = { }, dispenseFunctions: {}, registerDispenseFunction: function(namedID, func) { - var numericID = this.getNumericId(namedID); + var numericID = Item.getNumericId(namedID); if (numericID == -1) { return false; } @@ -2609,7 +2510,7 @@ var ItemRegistry = { Callback.invokeCallback("ItemUse", coords, item, block, false, entity); } if (noModCallback || !MCSystem.isDefaultPrevented()) { - Item.invokeItemUseOn(item.id, item.count, item.data, item.extra, coords.x, coords.y, coords.z, coords.side || 0, vec.x, vec.y, vec.z, entity); + Item.invokeUseItemOn(item.id, item.count, item.data, item.extra, coords.x, coords.y, coords.z, coords.side || 0, vec.x, vec.y, vec.z, entity); } }, invokeItemUseNoTarget: function(item, noModCallback) { @@ -2617,10 +2518,11 @@ var ItemRegistry = { Callback.invokeCallback("ItemUseNoTarget", item); } if (noModCallback || !MCSystem.isDefaultPrevented()) { - Item.invokeItemUseNoTarget(item.id, item.count, item.data, item.extra); + Item.invokeUseItemNoTarget(item.id, item.count, item.data, item.extra); } } }; +injectBindingsIntoScope("Item", ItemRegistry, false); Callback.addCallback("ProjectileHit", function(projectile, item, target) { if (item) { ItemRegistry.onProjectileHit(projectile, item, target); @@ -2651,18 +2553,16 @@ Callback.addCallback("ItemDispensed", function(coords, item, region, slot) { }); var ArmorRegistry = { registerFuncs: function(id, funcs) { - id = ItemRegistry.getNumericId(id); - if (id == -1) { - return; + id = Item.getNumericId(id); + if (id != -1) { + Armor.registerCallbacks(id, funcs); } - Armor.registerCallbacks(id, funcs); }, preventDamaging: function(id) { - id = ItemRegistry.getNumericId(id); - if (id == -1) { - return; + id = Item.getNumericId(id); + if (id != -1) { + Armor.preventDamaging(id); } - Armor.preventDamaging(id); }, registerOnTickListener: function(id, listener) { Armor.registerOnTickListener(id, listener); @@ -2736,12 +2636,15 @@ var ToolAPI = { } this.toolData[id] = params; if (!params.isNative) { - ItemRegistry.setMaxDamage(id, params.toolMaterial.durability); + Item.setMaxDamage(id, params.toolMaterial.durability); } }, registerSword: function(id, toolMaterial, params) { params = params || {}; params.isWeapon = true; + if (params.damage === undefined) { + params.damage = 4; + } this.registerTool(id, toolMaterial, ["fibre"], params); }, registerBlockMaterial: function(uid, materialName, level, isNative) { @@ -2933,7 +2836,10 @@ var ToolAPI = { item.id = toolData.brokenId; item.count = 1; item.data = 0; - WorldAPI.playSoundAtEntity(player, "random.break", 1); + var region = BlockSource.getDefaultForActor(player); + if (region) { + region.playSoundAtEntity(player, "random.break"); + } } } Entity.setCarriedItem(player, item.id, item.count, item.data, item.extra); @@ -2964,7 +2870,10 @@ var ToolAPI = { item.id = toolData.brokenId; item.count = 1; item.data = 0; - WorldAPI.playSoundAtEntity(attacker, "random.break", 1); + var region = BlockSource.getDefaultForActor(attacker); + if (region) { + region.playSoundAtEntity(attacker, "random.break"); + } } } var damage = toolData.damage + toolData.toolMaterial.damage; @@ -3393,65 +3302,7 @@ var WorldAPI = { Level.setBlockChangeCallbackEnabled(id, enabled); }, blockChangeCallbacks: [], - serverStateChangeCallbacks: [], - localStateChangeCallbacks: [], - - addListenerChunkStateChanged: function(listener, states) { - for(let i in states){ - let state = states[i]; - - let listeners = this.serverStateChangeCallbacks[state] || []; - listeners.push(listener); - this.serverStateChangeCallbacks[state] = listeners; - - Level.setChunkStateChangeCallbackEnabled(state); - } - }, - onChunkLoadingStateChanged: function(chunkX, chunkZ, dimension, preState, state, discarded) { - let listeners = this.serverStateChangeCallbacks[state]; - - for(let i in listeners) - listeners[i](chunkX, chunkZ, dimension, preState, state, discarded); - }, - - addLocalListenerChunkStateChanged: function(listener, states) { - for(let i in states){ - let state = states[i]; - - let listeners = this.localStateChangeCallbacks[state] || []; - listeners.push(listener); - this.localStateChangeCallbacks[state] = listeners; - - Level.setChunkStateChangeCallbackEnabled(state); - } - }, - onLocalChunkLoadingStateChanged: function(chunkX, chunkZ, dimension, preState, state, discarded) { - let listeners = this.localStateChangeCallbacks[state]; - - for(let i in listeners) - listeners[i](chunkX, chunkZ, dimension, preState, state, discarded); - }, - registerBlockChangeCallback: function(ids, callback) { - if (!Array.isArray(ids)) { - ids = [ids]; - } - for (var i in ids) { - var id = ids[i]; - if (typeof(id) == "string") { - var numericID = BlockRegistry.getNumericId(id); - if (numericID == -1) { - Logger.Log("invalid block name id " + id); - continue; - } - id = numericID; - } - Level.setBlockChangeCallbackEnabled(id, true); - var callbacks = this.blockChangeCallbacks[id] || []; - callbacks.push(callback); - this.blockChangeCallbacks[id] = callbacks; - } - }, registerBlockChangeCallback: function(ids, callback) { if (!Array.isArray(ids)) { ids = [ids]; @@ -3664,6 +3515,9 @@ var WorldAPI = { } Level.playSoundEnt(entity, name, volume, pitch); }, + stopSound: function(name) { + Level.stopSound(name); + }, getWorldDir: function() { return Level.getWorldDir(); }, @@ -3756,7 +3610,18 @@ var WorldAPI = { }, playSound: function(x, y, z, name, volume, pitch) {}, playSoundAtEntity: function(entity, name, volume, pitch) {} - } + }, + + // ====deprecated==== + serverStateChangeCallbacks: [], + localStateChangeCallbacks: [], + + addListenerChunkStateChanged() {}, + onChunkLoadingStateChanged() {}, + + addLocalListenerChunkStateChanged() {}, + onLocalChunkLoadingStateChanged() {} + }; WorldAPI.setLoaded(true); // always keep it loaded, for God sake, native will handle unloaded case @@ -3764,18 +3629,6 @@ Callback.addCallback("BlockChanged", function(coords, block1, block2, int1, int2 WorldAPI.onBlockChanged(coords, block1, block2, region, int1, int2); }); -Callback.addCallback("ChunkLoadingStateChanged", function(chunkX, chunkZ, dimension, preState, state, discarded){ - if(discarded) - WorldAPI.onChunkLoadingStateChanged(chunkX, chunkZ, dimension, preState, -1, discarded); - WorldAPI.onChunkLoadingStateChanged(chunkX, chunkZ, dimension, preState, state, discarded); -}); - -Callback.addCallback("LocalChunkLoadingStateChanged", function(chunkX, chunkZ, dimension, preState, state, discarded){ - if(discarded) - WorldAPI.onLocalChunkLoadingStateChanged(chunkX, chunkZ, dimension, preState, -1, discarded); - WorldAPI.onLocalChunkLoadingStateChanged(chunkX, chunkZ, dimension, preState, state, discarded); -}); - var AnimatorToken = { __current: 1, genToken: function() { @@ -5397,7 +5250,7 @@ var PlayerAPI = { count = 1; } var carried = this.getCarriedItem(true, true); - this.setCarriedItem(carried.id, carried.count - count, carried.data, carried.extra); + Player.setCarriedItem(carried.id, carried.count - count, carried.data, carried.extra); }, getInventorySlot: function(slot) { return Player.getInventorySlot(slot); @@ -5430,7 +5283,7 @@ var PlayerAPI = { }, addPosition: function(x, y, z) { var pos = this.getPosition(); - this.setPosition(pos.x + x, pos.y + y, pos.z + z); + Entity.setPosition(getPlayerEnt(), pos.x + x, pos.y + y, pos.z + z); }, setVelocity: function(x, y, z) { Entity.setVelocity(getPlayerEnt(), x, y, z); @@ -5445,7 +5298,7 @@ var PlayerAPI = { }, addVelocity: function(x, y, z) { var vel = this.getVelocity(); - this.setVelocity(vel.x + x, vel.y + y, vel.z + z); + Entity.setVelocity(getPlayerEnt(), vel.x + x, vel.y + y, vel.z + z); }, experience: function() { return { @@ -5477,7 +5330,7 @@ var PlayerAPI = { Player.setLevel(level); }, addLevel: function(level) { - this.setLevel(this.getLevel() + level); + Player.setLevel(Player.getLevel() + level); }, flying: function() { return { @@ -5555,12 +5408,6 @@ var PlayerAPI = { getScore: function() { return Player.getScore(); }, - setFov: function(fov) { - Player.setFov(fov); - }, - resetFov: function() { - Player.resetFov(); - }, setCameraEntity: function(ent) { Player.setCameraEntity(ent); }, @@ -5577,6 +5424,7 @@ var PlayerAPI = { return Player.getBooleanAbility(ability); } }; +injectBindingsIntoScope("Player", PlayerAPI, false); var ANIMATION_BASE_ENTITY = 10; var AnimationRegistry = { animationList: [], @@ -5852,7 +5700,7 @@ function AnimationItem(x, y, z) { var lastMesh = this.__itemMesh; var itemModel = ItemModel.getForWithFallback(item.id, item.data); - var glint = item.glint || Item.isGlintItemInstance(item.id, item.data, item.extra); + var glint = item.glint || Item.isGlint(item.id, item.data, item.extra); var material = glint ? itemModel.getWorldGlintMaterialName() : itemModel.getWorldMaterialName(); this.__itemMesh = itemModel.getItemRenderMesh(item.count > 1 ? (item.count > 20 ? 3 : 2) : 1, !item.notRandomize); @@ -5985,13 +5833,32 @@ Callback.addCallback("EntityAdded", function(entity) { // ------------------------------------------------------ var EntityAPI = { + isSleeping: function(ent) { + return Entity.isSleeping(ent); + }, + playAnimation: function(ent, name, time, controller, query, hz) { + Entity.playAnimation(ent, name, time, controller || "__runtime_controller", query || "query.any_animation_finished", hz || "default"); + }, + getAABB: function(ent) { + var aabb = Entity.getAABB(ent); + return { + x1: aabb[0], + y1: aabb[1], + z1: aabb[2], + x2: aabb[3], + y2: aabb[4], + z2: aabb[5] + }; + }, getAll: function() { return Entity.getAll(); }, getAllJS: function() { return Entity.getAll(); }, - playAnimation() {},//client only + getAllLocal: function() { + return Entity.getAllLocal(); + }, getExtra: function(ent, name) { logDeprecation("Entity.getExtra"); return null; @@ -6019,7 +5886,7 @@ var EntityAPI = { return { level: Entity.getEffectLevel(ent, effectId), duration: Entity.getEffectDuration(ent, effectId) - } + }; }, clearEffect: function(ent, id) { Entity.removeEffect(ent, id); @@ -6144,7 +6011,7 @@ var EntityAPI = { return Entity.setTarget(ent, target); }, getMobile: function(ent, mobile) { - Entity.isImmobile(ent); + return !Entity.isImmobile(ent); }, setMobile: function(ent, mobile) { Entity.setImmobile(ent, !mobile); @@ -6164,19 +6031,19 @@ var EntityAPI = { setFire: function(ent, fire, force) { Entity.setFireTicks(ent, fire || 0, force); }, - health: function(entity) { + health: function(ent) { return { get: function() { - return this.getHealth(entity); + return Entity.getHealth(ent); }, set: function(health) { - this.setHealth(entity, health); + Entity.setHealth(ent, health); }, getMax: function() { - return this.getMaxHealth(entity); + return Entity.getMaxHealth(ent); }, setMax: function(health) { - this.setMaxHealth(entity, health); + Entity.setMaxHealth(ent, health); } }; }, @@ -6205,7 +6072,7 @@ var EntityAPI = { }, addPosition: function(ent, x, y, z) { var pos = this.getPosition(ent); - this.setPosition(ent, pos.x + x, pos.y + y, pos.z + z); + Entity.setPosition(ent, pos.x + x, pos.y + y, pos.z + z); }, setVelocity: function(ent, x, y, z) { Entity.setVelocity(ent, x, y, z); @@ -6220,7 +6087,7 @@ var EntityAPI = { }, addVelocity: function(ent, x, y, z) { var vel = this.getVelocity(ent); - this.setVelocity(ent, vel.x + x, vel.y + y, vel.z + z); + Entity.setVelocity(ent, vel.x + x, vel.y + y, vel.z + z); }, getDistanceBetweenCoords: function(coords1, coords2) { return Math.sqrt(Math.pow(coords1.x - coords2.x, 2) + Math.pow(coords1.y - coords2.y, 2) + Math.pow(coords1.z - coords2.z, 2)); @@ -6439,19 +6306,30 @@ Callback.addCallback("NativeCommand", function(commandString) { }); var CONSTANT_REPLACEABLE_TILES = { - 0: true, - 8: true, - 9: true, - 10: true, - 11: true, - 31: true, - 51: true, - 78: true, - 106: true + 0: true, // air + 8: true, 9: true, // water + 10: true, 11: true, // lava + 31: true, // tallgrass (fern) + 32: true, // deadbush + 51: true, // fire + 106: true, // vine + + // 1.11+ + 385: true, // seagrass + + // 1.16+ + 497: true, // crimson roots + 509: true, // light block + 512: true, // nether sprouts + 540: true, // structure void + 554: true, // warped roots }; function canTileBeReplaced(id, data) { + // 175: double plant (tall grass) if (id === 175 && (data % 8 === 2 || data % 8 === 3)) return true; + // 78: snow layer (1st layer + 2nd-7th which is bug) + if (id === 78 && data <= 6) return true; return CONSTANT_REPLACEABLE_TILES[id] || false; } @@ -6460,56 +6338,65 @@ var CONSTANT_VANILLA_UI_TILES = { 25: true, // note block 26: true, // bed 54: true, // chest - 58: true, // workbench + 58: true, // crafting table 61: true, 62: true, // furnace - 64: true, // door + 64: true, 193: true, 194: true, 195: true, 196: true, 197: true, // wooden doors 69: true, // lever 77: true, // stone button 84: true, // jukebox 92: true, // cake 93: true, 94: true, // repeater - 96: true, // wooden trapdoor - 107: true, // fence gate + 96: true, 400: true, 401: true, 402: true, 403: true, 404: true, // wooden trapdoors + 107: true, 183: true, 184: true, 185: true, 186: true, 187: true, // fence gates 116: true, // enchanting table 117: true, // brewing stand 122: true, // dragon egg 125: true, // dropper 130: true, // ender chest + 137: true, 188: true, 189: true, // command blocks 138: true, // beacon - 143: true, // wooden button + 143: true, 395: true, 396: true, 397: true, 398: true, 399: true, // wooden buttons 145: true, // anvil 146: true, // trapped chest 149: true, 150: true, // comparator - 151: true, 178: true, // day light detector + 151: true, 178: true, // daylight detector 154: true, // hopper - 183: true, 184: true, 185: true, 186: true, 187: true, // fence gates - 193: true, 194: true, 195: true, 196: true, 197: true, // wooden doors + 199: true, // item frame 205: true, 218: true, // shulker box - 395: true, 396: true, 397: true, 398: true, 399: true, // wooden buttons - 400: true, 401: true, 402: true, 403: true, 404: true, // wooden trapdoors + 247: true, // nether reactor + 252: true, // structure block + + // 1.11+ 449: true, // lectern 450: true, // grindstone 451: true, 469: true, // blast furnace 452: true, // stonecutter 453: true, 454: true, // smoker 455: true, // cartography table - 457: true, // smiting table + 457: true, // smithing table 458: true, // barrel 459: true, // loom 461: true, // bell - 499: true, // crimson door - 500: true, // warped door - 559: true, // crimson trapdoor - 502: true, // warped trapdoor - 513: true, // crimson fence gate - 514: true, // warped fence gate - 515: true, // crimson button - 516: true, // warped button - 551: true, // polished blackstone button + 462: true, // sweet berry bush + 464: true, // campfire + 466: true, // jigsaw + + // 1.16+ + 487: true, // crimson button + 488: true, // crimson door + 491: true, // crimson fence gate + 502: true, // crimson trapdoor + 528: true, // respawn anchor + 530: true, // soul campfire + 544: true, // warped button + 545: true, // warped door + 548: true, // warped fence gate + 559: true, // warped trapdoor + 521: true, // polished blackstone button }; function doesVanillaTileHasUI(id) { - return CONSTANT_VANILLA_UI_TILES[id]; + return CONSTANT_VANILLA_UI_TILES[id] || false; } function setPlayerItem(id, count, data, extra) { @@ -6530,48 +6417,83 @@ Callback.addCallback("ItemUse", function(coords, item, block, isExternal, player } if (Entity.isSneaking(player) || !doesVanillaTileHasUI(block.id)) { - let placeCoords = null; + let inplaceFunc = BlockRegistry.getInplaceFunc(item.id); let placeFunc = BlockRegistry.getPlaceFunc(item.id); - if (placeFunc) { - placeCoords = placeFunc(coords, item, block, player, blockSource); - } - let isTileEntity = TileEntity.isTileEntityBlock(item.id); - if (isTileEntity) { - if (!placeFunc) { - if (canTileBeReplaced(block.id, block.data)) { - placeCoords = coords; + if (inplaceFunc || placeFunc) { + let placeCoords = null; + if (canTileBeReplaced(block.id, block.data)) { + placeCoords = coords; + } else { + let tile = blockSource.getBlock(coords.relative.x, coords.relative.y, coords.relative.z); + if (canTileBeReplaced(tile.id, tile.data)) { + placeCoords = coords.relative; + } + } + + let previousItemCount = item.count; + if (inplaceFunc) { + // Modern approach to directly determine when block is actually being set + placeCoords = inplaceFunc(coords, placeCoords, item, block, player, blockSource); + } else if (placeCoords) { + let relativeBlock = null; + if (coords.x != placeCoords.x || coords.y != placeCoords.y || coords.z != placeCoords.z) { + relativeBlock = blockSource.getBlock(placeCoords.x, placeCoords.y, placeCoords.z); + } + + // Allow place function to be called only if specified location is not obstructed + let preferredCoords = ModAPI.cloneObject(coords, true); + preferredCoords.relative = { x: placeCoords.x, y: placeCoords.y, z: placeCoords.z }; + preferredCoords = placeFunc(preferredCoords, item, block, player, blockSource); + + // Block is explicitly set on specified coords + if (preferredCoords) { + placeCoords = preferredCoords; + + // Observing dedupe, actually unchanged location, stealing unconsumed items } else { - let tile = blockSource.getBlock(coords.relative.x, coords.relative.y, coords.relative.z); - if (canTileBeReplaced(tile.id, tile.data)) { - placeCoords = coords.relative; + // Block set is being forcibly cancelled by increasing item count + if (item.count > previousItemCount) { + item.count--; + placeCoords = null; + } else { + let newItem = Entity.getCarriedItem(player); + if (item.count >= previousItemCount && newItem.count >= previousItemCount) { + // Manually recheck that block is being changed or cancelled within function + let newBlock = blockSource.getBlock(coords.x, coords.y, coords.z); + if (block == newBlock) { + let newRelativeBlock = relativeBlock ? blockSource.getBlock(placeCoords.x, placeCoords.y, placeCoords.z) : null; + if (!newRelativeBlock || newRelativeBlock == relativeBlock) { + placeCoords = null; + } + } + } } } - if (placeCoords) { - blockSource.setBlock(placeCoords.x, placeCoords.y, placeCoords.z, item.id, item.data); - } } - if (placeCoords) { - TileEntity.addTileEntity(placeCoords.x, placeCoords.y, placeCoords.z, blockSource); - } - } - if (placeCoords) { - if (GameAPI.isItemSpendingAllowed(player)) { - if (item.count > 1) { - Entity.setCarriedItem(player, item.id, item.count - 1, item.data, item.extra); + if (placeCoords) { + if (TileEntity.isTileEntityBlock(item.id)) { + TileEntity.addTileEntity(placeCoords.x, placeCoords.y, placeCoords.z, blockSource); + } + if (GameAPI.isItemSpendingAllowed(player)) { + if (item.count > 1) { + Entity.setCarriedItem(player, item.id, item.count - 1, item.data, item.extra); + } else { + Entity.setCarriedItem(player, 0, 0, 0); + } } else { - Entity.setCarriedItem(player, 0, 0, 0); + Entity.setCarriedItem(player, item.id, previousItemCount, item.data, item.extra); } + blockSource.playSound(placeCoords.x, placeCoords.y, placeCoords.z, "dig.stone", 1, 0.8); + preventDefault(); + return; } - blockSource.playSound(placeCoords.x, placeCoords.y, placeCoords.z, "dig.stone", 1, 0.8); - preventDefault(); - } else { - ItemRegistry.onItemUsed(coords, item, block, player); } + + ItemRegistry.onItemUsed(coords, item, block, player); } }); - var EntityDataRegistry = { isLevelLoaded: false, entityData: {}, @@ -7152,9 +7074,14 @@ var CoreAPI = { Saver: SaverAPI, World: WorldAPI, Entity: EntityAPI, + PathNavigation: PathNavigation, // codegen AddonEntityRegistry: AddonEntityRegistry, Player: PlayerAPI, Game: GameAPI, + GameController: GameController, + + SamplingProfiler: SamplingProfiler, + Render: RenderAPI, Texture: Texture, EntityModel: ModelAPI, @@ -7170,6 +7097,7 @@ var CoreAPI = { Swim: EntityAISwim, PanicWatcher: EntityAIPanicWatcher }, + GenerationUtils: WorldGenerationUtils, Animation: { base: AnimationBase, diff --git a/src/main/resources/innercore/coreengine/data-scripts/16/blocks-and-tools.js b/src/main/resources/innercore/coreengine/data-scripts/16/blocks-and-tools.js index b28612074..9287b2ab4 100755 --- a/src/main/resources/innercore/coreengine/data-scripts/16/blocks-and-tools.js +++ b/src/main/resources/innercore/coreengine/data-scripts/16/blocks-and-tools.js @@ -397,7 +397,10 @@ BlockRegistry.registerDropFunctionForID(VanillaTileID.grass, function(coords, id BlockRegistry.setDestroyLevelForID(VanillaTileID.cobblestone, 1); BlockRegistry.registerDropFunctionForID(VanillaTileID.gravel, function(coords, id, data, level, enchant) { - if (Math.random() < 1 / (10 - enchant.fortune * 3)) { + if (enchant.silk) { + return [[id, 1, 0]]; + } + if (enchant.fortune > 2 || Math.random() < 1 / (10 - enchant.fortune * 3)) { return [[VanillaItemID.flint, 1, 0]]; } return [[id, 1, 0]]; @@ -409,7 +412,7 @@ BlockRegistry.registerDropFunctionForID(VanillaTileID.gold_ore, function(coords, ToolAPI.dropOreExp(coords, 2, 3, enchant.experience, blockSource); return [[VanillaItemID.gold_ingot, 1, 0]]; } else { - return [[VanillaTileID.gold_ore, 1, 0]] + return [[VanillaTileID.gold_ore, 1, 0]]; } } return []; @@ -421,7 +424,7 @@ BlockRegistry.registerDropFunctionForID(VanillaTileID.iron_ore, function(coords, ToolAPI.dropOreExp(coords, 2, 2, enchant.experience, blockSource); return [[VanillaItemID.iron_ingot, 1, 0]]; } else { - return [[VanillaTileID.iron_ore, 1, 0]] + return [[VanillaTileID.iron_ore, 1, 0]]; } } return []; @@ -621,7 +624,7 @@ BlockRegistry.setDestroyLevelForID(VanillaTileID.end_stone, 1); BlockRegistry.setDestroyLevelForID(VanillaTileID.dropper, 1, true); BlockRegistry.setDestroyLevelForID(VanillaTileID.sandstone_stairs, 1, true); -BlockRegistry.registerDropFunctionForID(VanillaTileID.emerald_block, function(coords, id, data, level, enchant, item, blockSource) { +BlockRegistry.registerDropFunctionForID(VanillaTileID.emerald_ore, function(coords, id, data, level, enchant, item, blockSource) { if (level >= 3) { if (enchant.silk) { return [[id, 1, data]]; @@ -937,7 +940,10 @@ BlockRegistry.setDestroyLevelForID(VanillaTileID.crying_obsidian, 4); BlockRegistry.registerDropFunctionForID(VanillaTileID.gilded_blackstone, function(coords, id, data, level, enchant) { if (level >= 1) { - if (Math.random() < 1 / (10 - enchant.fortune * 3)) { + if (enchant.silk) { + return [[BlockRegistry.convertBlockToItemId(id), 1, 0]]; + } + if (enchant.fortune > 2 || Math.random() < 1 / (10 - enchant.fortune * 3)) { let drop = []; let count = 2 + Math.floor(Math.random() * 4); for (let i = 0; i < count; i++) { @@ -1003,12 +1009,12 @@ BlockRegistry.setDestroyLevelForID(VanillaTileID.soul_lantern, 1, true); BlockRegistry.setDestroyLevelForID(VanillaTileID.respawn_anchor, 4, true); BlockRegistry.registerDropFunctionForID(VanillaTileID.web, function(coords, id, data, level, enchant, item) { - if(enchant.silk){ + if (enchant.silk){ return [[id, 1, 0]]; - } - if(level >= 1){ + } + if (level >= 1){ var toolData = ToolAPI.getToolData(item.id); - if(toolData && toolData.isWeapon){ + if (toolData && toolData.isWeapon){ return [[VanillaItemID.string, 1, 0]]; } else { return [[id, 1, 0]]; diff --git a/src/test/java/org/innercore/icstd/CodeGen.java b/src/test/java/org/innercore/icstd/CodeGen.java index 954a66752..db8333687 100644 --- a/src/test/java/org/innercore/icstd/CodeGen.java +++ b/src/test/java/org/innercore/icstd/CodeGen.java @@ -2,10 +2,13 @@ import com.zhekasmirnov.innercore.api.gen.CodeGenBindGenBase; import org.innercore.icstd.annotations.ApiBinding; +import org.innercore.icstd.annotations.ArgOverload; +import org.jetbrains.annotations.Nullable; @ApiBinding public class CodeGen extends CodeGenBindGenBase { @ApiBinding public int test; + @ApiBinding @Nullable public Object test2; @ApiBinding public CodeGen() { @@ -28,12 +31,7 @@ public String other(String a, int b, CodeGen c) { } @ApiBinding - public static int staticSum(int a, int b) { - return a + b; - } - - @ApiBinding - public static int staticSum(int a, int b, int c) { + public static int staticSum(int a, int b, @ArgOverload("0") int c) { return a + b + c; } diff --git a/src/test/java/org/innercore/icstd/test/CodeGenTest.java b/src/test/java/org/innercore/icstd/test/CodeGenTest.java index aedc98528..20586f541 100644 --- a/src/test/java/org/innercore/icstd/test/CodeGenTest.java +++ b/src/test/java/org/innercore/icstd/test/CodeGenTest.java @@ -40,5 +40,15 @@ public void test() { self.put("test", self, 1); assert (other.call(null, null, self, new Object[]{"test", "5", self})).toString().equals("test51"); + + self.put("test", self, null); + + assert (other.call(null, null, self, new Object[]{"test", "5", self})).toString().equals("test50"); + + self.put("test", self, null); + + assert (other.call(null, null, self, new Object[]{"test", "5", self})).toString().equals("test50"); + + self.put("test2", self, null); } }