Condensation drive test
This commit is contained in:
parent
d7a62f6d3c
commit
e7e12bed8c
@ -15,8 +15,10 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import ru.dbotthepony.mc.otm.capability.AndroidCapability;
|
import ru.dbotthepony.mc.otm.capability.AndroidCapability;
|
||||||
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
|
import ru.dbotthepony.mc.otm.capability.AndroidCapabilityPlayer;
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||||
|
import ru.dbotthepony.mc.otm.capability.drive.DrivePool;
|
||||||
import ru.dbotthepony.mc.otm.client.AndroidGui;
|
import ru.dbotthepony.mc.otm.client.AndroidGui;
|
||||||
import ru.dbotthepony.mc.otm.client.EventHandler;
|
import ru.dbotthepony.mc.otm.client.EventHandler;
|
||||||
|
import ru.dbotthepony.mc.otm.item.ItemPortableCondensationDrive;
|
||||||
import ru.dbotthepony.mc.otm.matter.MatterGrid;
|
import ru.dbotthepony.mc.otm.matter.MatterGrid;
|
||||||
import ru.dbotthepony.mc.otm.matter.MatterRegistry;
|
import ru.dbotthepony.mc.otm.matter.MatterRegistry;
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
|
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
|
||||||
@ -58,6 +60,9 @@ public class OverdriveThatMatters {
|
|||||||
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.AndroidResearch.class);
|
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.AndroidResearch.class);
|
||||||
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.Stats.class);
|
FMLJavaModLoadingContext.get().getModEventBus().register(Registry.Stats.class);
|
||||||
|
|
||||||
|
MinecraftForge.EVENT_BUS.register(DrivePool.class);
|
||||||
|
MinecraftForge.EVENT_BUS.register(ItemPortableCondensationDrive.class);
|
||||||
|
|
||||||
FMLJavaModLoadingContext.get().getModEventBus().addListener(AndroidCapability::registerEffects);
|
FMLJavaModLoadingContext.get().getModEventBus().addListener(AndroidCapability::registerEffects);
|
||||||
|
|
||||||
// LOGGER.info("Registered event handlers");
|
// LOGGER.info("Registered event handlers");
|
||||||
|
@ -29,10 +29,7 @@ import ru.dbotthepony.mc.otm.android.feature.AndroidNanobotsRegeneration;
|
|||||||
import ru.dbotthepony.mc.otm.block.*;
|
import ru.dbotthepony.mc.otm.block.*;
|
||||||
import ru.dbotthepony.mc.otm.block.entity.*;
|
import ru.dbotthepony.mc.otm.block.entity.*;
|
||||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
||||||
import ru.dbotthepony.mc.otm.item.ItemBattery;
|
import ru.dbotthepony.mc.otm.item.*;
|
||||||
import ru.dbotthepony.mc.otm.item.ItemMatterCapacitor;
|
|
||||||
import ru.dbotthepony.mc.otm.item.ItemPatternStorage;
|
|
||||||
import ru.dbotthepony.mc.otm.item.ItemPill;
|
|
||||||
import ru.dbotthepony.mc.otm.menu.*;
|
import ru.dbotthepony.mc.otm.menu.*;
|
||||||
import ru.dbotthepony.mc.otm.screen.*;
|
import ru.dbotthepony.mc.otm.screen.*;
|
||||||
|
|
||||||
@ -113,6 +110,8 @@ public class Registry {
|
|||||||
|
|
||||||
public static final ResourceLocation NUTRIENT_PASTE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nutrient_paste");
|
public static final ResourceLocation NUTRIENT_PASTE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "nutrient_paste");
|
||||||
|
|
||||||
|
public static final ResourceLocation PORTABLE_CONDENSATION_DRIVE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "portable_condensation_drive");
|
||||||
|
|
||||||
// android features and research
|
// android features and research
|
||||||
public static final ResourceLocation AIR_BAGS = new ResourceLocation(OverdriveThatMatters.MOD_ID, "air_bags");
|
public static final ResourceLocation AIR_BAGS = new ResourceLocation(OverdriveThatMatters.MOD_ID, "air_bags");
|
||||||
|
|
||||||
@ -234,6 +233,8 @@ public class Registry {
|
|||||||
public static final ItemPatternStorage PATTERN_DRIVE_NORMAL = new ItemPatternStorage(4);
|
public static final ItemPatternStorage PATTERN_DRIVE_NORMAL = new ItemPatternStorage(4);
|
||||||
public static final ItemPatternStorage PATTERN_DRIVE_CREATIVE = new ItemPatternStorage();
|
public static final ItemPatternStorage PATTERN_DRIVE_CREATIVE = new ItemPatternStorage();
|
||||||
|
|
||||||
|
public static final ItemPortableCondensationDrive PORTABLE_CONDENSATION_DRIVE = new ItemPortableCondensationDrive();
|
||||||
|
|
||||||
public static final Item NUTRIENT_PASTE = new Item(new Item.Properties().stacksTo(64).food(new FoodProperties.Builder().meat().nutrition(8).saturationMod(0.9F).build()).tab(OverdriveThatMatters.CREATIVE_TAB));
|
public static final Item NUTRIENT_PASTE = new Item(new Item.Properties().stacksTo(64).food(new FoodProperties.Builder().meat().nutrition(8).saturationMod(0.9F).build()).tab(OverdriveThatMatters.CREATIVE_TAB));
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -262,6 +263,7 @@ public class Registry {
|
|||||||
|
|
||||||
PATTERN_DRIVE_NORMAL.setRegistryName(Names.PATTERN_DRIVE_NORMAL);
|
PATTERN_DRIVE_NORMAL.setRegistryName(Names.PATTERN_DRIVE_NORMAL);
|
||||||
PATTERN_DRIVE_CREATIVE.setRegistryName(Names.PATTERN_DRIVE_CREATIVE);
|
PATTERN_DRIVE_CREATIVE.setRegistryName(Names.PATTERN_DRIVE_CREATIVE);
|
||||||
|
PORTABLE_CONDENSATION_DRIVE.setRegistryName(Names.PORTABLE_CONDENSATION_DRIVE);
|
||||||
|
|
||||||
NUTRIENT_PASTE.setRegistryName(Names.NUTRIENT_PASTE);
|
NUTRIENT_PASTE.setRegistryName(Names.NUTRIENT_PASTE);
|
||||||
}
|
}
|
||||||
@ -293,6 +295,7 @@ public class Registry {
|
|||||||
|
|
||||||
event.getRegistry().register(PATTERN_DRIVE_NORMAL);
|
event.getRegistry().register(PATTERN_DRIVE_NORMAL);
|
||||||
event.getRegistry().register(PATTERN_DRIVE_CREATIVE);
|
event.getRegistry().register(PATTERN_DRIVE_CREATIVE);
|
||||||
|
event.getRegistry().register(PORTABLE_CONDENSATION_DRIVE);
|
||||||
|
|
||||||
event.getRegistry().register(NUTRIENT_PASTE);
|
event.getRegistry().register(NUTRIENT_PASTE);
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import net.minecraftforge.common.capabilities.Capability;
|
|||||||
import net.minecraftforge.common.capabilities.CapabilityInject;
|
import net.minecraftforge.common.capabilities.CapabilityInject;
|
||||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||||
import net.minecraftforge.energy.IEnergyStorage;
|
import net.minecraftforge.energy.IEnergyStorage;
|
||||||
|
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
@ -28,6 +29,9 @@ public class MatteryCapability {
|
|||||||
@CapabilityInject(IMatterTaskProvider.class)
|
@CapabilityInject(IMatterTaskProvider.class)
|
||||||
public static Capability<IMatterTaskProvider> TASK = null;
|
public static Capability<IMatterTaskProvider> TASK = null;
|
||||||
|
|
||||||
|
@CapabilityInject(IMatteryDrive.class)
|
||||||
|
public static Capability<IMatteryDrive> DRIVE = null;
|
||||||
|
|
||||||
public static void register() {
|
public static void register() {
|
||||||
CapabilityManager.INSTANCE.register(IAndroidCapability.class);
|
CapabilityManager.INSTANCE.register(IAndroidCapability.class);
|
||||||
CapabilityManager.INSTANCE.register(IMatteryEnergyStorage.class);
|
CapabilityManager.INSTANCE.register(IMatteryEnergyStorage.class);
|
||||||
@ -35,6 +39,7 @@ public class MatteryCapability {
|
|||||||
CapabilityManager.INSTANCE.register(IPatternStorage.class);
|
CapabilityManager.INSTANCE.register(IPatternStorage.class);
|
||||||
CapabilityManager.INSTANCE.register(IMatterGridCell.class);
|
CapabilityManager.INSTANCE.register(IMatterGridCell.class);
|
||||||
CapabilityManager.INSTANCE.register(IMatterTaskProvider.class);
|
CapabilityManager.INSTANCE.register(IMatterTaskProvider.class);
|
||||||
|
CapabilityManager.INSTANCE.register(IMatteryDrive.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final MathContext ROUND_RULES = new MathContext(32, RoundingMode.HALF_DOWN);
|
public static final MathContext ROUND_RULES = new MathContext(32, RoundingMode.HALF_DOWN);
|
||||||
|
@ -0,0 +1,267 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.drive;
|
||||||
|
|
||||||
|
import net.minecraft.CrashReport;
|
||||||
|
import net.minecraft.ReportedException;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtIo;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
|
||||||
|
import net.minecraftforge.fmlserverevents.FMLServerStoppingEvent;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.security.Provider;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let me answer everyone's questions about:
|
||||||
|
*
|
||||||
|
* Why?
|
||||||
|
*
|
||||||
|
* There are several reasons:
|
||||||
|
* 0. This data can get very large very quickly, even when playing singleplayer (and much quicker and bigger when hosting a dedicated server)
|
||||||
|
* 1. This data can not be stored inside ItemStack.ForgeCaps due to it's size
|
||||||
|
* 2. This data can not be stored inside unshared (server only) nbt tag because, again, mods prone to use and interact with
|
||||||
|
* it wrong, causing loss of stored data or mods exposing full content of a drive inside their own tag (which cause very real "NBT size too large"
|
||||||
|
* network kicks, often locking players out of server
|
||||||
|
* 3. net.minecraft.world.level.saveddata.SaveData is for storing everything inside one dat file, which
|
||||||
|
* is performance tanking, because we have to write *entire* NBT on each save, not the data of Drive which got changed
|
||||||
|
*/
|
||||||
|
public class DrivePool {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
|
||||||
|
public static class Entry {
|
||||||
|
public final UUID id;
|
||||||
|
public final IMatteryDrive drive;
|
||||||
|
public long last_access = System.currentTimeMillis();
|
||||||
|
public long soft_sync_on = last_access + 8_000; // soft sync in 4 seconds
|
||||||
|
public long hard_sync_on = last_access + 30_000; // hard sync in 4 seconds
|
||||||
|
|
||||||
|
Entry(UUID id, IMatteryDrive drive) {
|
||||||
|
this.id = id;
|
||||||
|
this.drive = drive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public CompoundTag sync(boolean force) {
|
||||||
|
if (!drive.isDirty()) {
|
||||||
|
soft_sync_on = System.currentTimeMillis() + 8_000;
|
||||||
|
hard_sync_on = System.currentTimeMillis() + 30_000;
|
||||||
|
next_save = Math.min(next_save, soft_sync_on);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force || hard_sync_on <= System.currentTimeMillis() || soft_sync_on <= System.currentTimeMillis()) {
|
||||||
|
var tag = drive.serializeNBT();
|
||||||
|
drive.markClean();
|
||||||
|
soft_sync_on = System.currentTimeMillis() + 8_000;
|
||||||
|
hard_sync_on = System.currentTimeMillis() + 30_000;
|
||||||
|
next_save = Math.min(next_save, soft_sync_on);
|
||||||
|
last_access = System.currentTimeMillis();
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMatteryDrive access() {
|
||||||
|
last_access = System.currentTimeMillis();
|
||||||
|
soft_sync_on = last_access + 8_000;
|
||||||
|
next_save = Math.min(hard_sync_on, Math.min(next_save, soft_sync_on));
|
||||||
|
return drive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashMap<UUID, Entry> pool = new HashMap<>();
|
||||||
|
private static Thread thread;
|
||||||
|
private static boolean stopping = false;
|
||||||
|
private static ReportedException exception;
|
||||||
|
public static final String DATA_PATH = "data/otm_drives";
|
||||||
|
|
||||||
|
private static long next_save = Long.MAX_VALUE;
|
||||||
|
|
||||||
|
public static <T extends IMatteryDrive> T get(UUID id, Function<CompoundTag, T> factory_stored, Supplier<T> factory_empty) {
|
||||||
|
var get = pool.get(id);
|
||||||
|
|
||||||
|
if (get != null) {
|
||||||
|
return (T) get.access();
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = new File(base_directory, id.toString() + ".dat");
|
||||||
|
|
||||||
|
if (file.exists()) {
|
||||||
|
try {
|
||||||
|
var factorize = factory_stored.apply(NbtIo.readCompressed(file));
|
||||||
|
pool.put(id, new Entry(id, factorize));
|
||||||
|
return factorize;
|
||||||
|
} catch(Throwable error) {
|
||||||
|
var report = new CrashReport("Reading OTM Drive from disk", error);
|
||||||
|
var category = report.addCategory("Corrupt drive");
|
||||||
|
category.setDetail("UUID", id);
|
||||||
|
throw new ReportedException(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var factorize = factory_empty.get();
|
||||||
|
pool.put(id, new Entry(id, factorize));
|
||||||
|
|
||||||
|
return factorize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void put(UUID id, IMatteryDrive drive) {
|
||||||
|
pool.put(id, new Entry(id, drive));
|
||||||
|
}
|
||||||
|
|
||||||
|
private record BacklogLine(UUID file, CompoundTag tag, IMatteryDrive drive) {
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj instanceof BacklogLine line && line.file.equals(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<BacklogLine> backlog = new ArrayList<>();
|
||||||
|
private static File base_directory;
|
||||||
|
|
||||||
|
private static void thread(MinecraftServer server) {
|
||||||
|
while (true) {
|
||||||
|
LockSupport.park();
|
||||||
|
|
||||||
|
if (stopping) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
sync();
|
||||||
|
} catch (ReportedException error) {
|
||||||
|
exception = error;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onServerPostTick(TickEvent.ServerTickEvent event) {
|
||||||
|
if (event.phase == TickEvent.Phase.END) {
|
||||||
|
if (exception != null) {
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (next_save < System.currentTimeMillis()) {
|
||||||
|
writeBacklog(true);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void serverStartEvent(FMLServerStartedEvent event) {
|
||||||
|
if (thread != null && thread.isAlive()) {
|
||||||
|
LOGGER.error("Attempting to start another DrivePool I/O thread when already running one!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.clear();
|
||||||
|
|
||||||
|
final var server = event.getServer();
|
||||||
|
base_directory = new File(server.storageSource.getWorldDir().toFile(), DATA_PATH);
|
||||||
|
base_directory.mkdirs();
|
||||||
|
|
||||||
|
thread = new Thread(null, () -> DrivePool.thread(server), "Overdrive That Matters DrivePool I/O");
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void serverStopEvent(FMLServerStoppingEvent event) {
|
||||||
|
if (thread != null && thread.isAlive()) {
|
||||||
|
stopping = true;
|
||||||
|
LockSupport.unpark(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBacklog(true);
|
||||||
|
sync();
|
||||||
|
|
||||||
|
pool.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeBacklog(boolean force) {
|
||||||
|
var sync_required = false;
|
||||||
|
ArrayList<UUID> remove_keys = null;
|
||||||
|
|
||||||
|
for (var entry : pool.entrySet()) {
|
||||||
|
var tag = entry.getValue().sync(force);
|
||||||
|
|
||||||
|
if (tag != null) {
|
||||||
|
LOGGER.info("Syncing {}", entry.getKey());
|
||||||
|
|
||||||
|
var index = backlog.indexOf(entry.getKey());
|
||||||
|
|
||||||
|
if (index != -1) {
|
||||||
|
backlog.set(index, new BacklogLine(entry.getKey(), tag, entry.getValue().drive));
|
||||||
|
} else {
|
||||||
|
backlog.add(new BacklogLine(entry.getKey(), tag, entry.getValue().drive));
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_required = true;
|
||||||
|
} else if (entry.getValue().last_access < System.currentTimeMillis() - 600_000) {
|
||||||
|
// no access or changes in 600 seconds - unload
|
||||||
|
if (remove_keys == null)
|
||||||
|
remove_keys = new ArrayList<>();
|
||||||
|
|
||||||
|
remove_keys.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remove_keys != null) {
|
||||||
|
for (var key : remove_keys) {
|
||||||
|
pool.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync_required) {
|
||||||
|
LockSupport.unpark(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pool.size() == 0) {
|
||||||
|
next_save = Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sync() {
|
||||||
|
var copy = backlog;
|
||||||
|
backlog = new ArrayList<>();
|
||||||
|
|
||||||
|
for (var entry : copy) {
|
||||||
|
try {
|
||||||
|
var file = new File(base_directory, entry.file.toString() + ".dat");
|
||||||
|
LOGGER.info("Writing {}", entry.file);
|
||||||
|
NbtIo.writeCompressed(entry.tag, file);
|
||||||
|
} catch(Throwable error) {
|
||||||
|
var report = new CrashReport("Syncing OTM Drives state to disk", error);
|
||||||
|
var category = report.addCategory("Corrupt drive");
|
||||||
|
category.setDetail("UUID", entry.file.toString());
|
||||||
|
category.setDetail("Stored item count", entry.drive.getStoredCount());
|
||||||
|
category.setDetail("Capacity", entry.drive.getCapacity());
|
||||||
|
|
||||||
|
if (entry.drive instanceof MatteryDrive drive) {
|
||||||
|
category.setDetail("Amount of different stacks", drive.different_stacks);
|
||||||
|
category.setDetail("Max amount of different stacks", drive.max_different_stacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ReportedException(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.drive;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.common.util.INBTSerializable;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public interface IMatteryDrive {
|
||||||
|
List<StoredStack> getItems();
|
||||||
|
|
||||||
|
boolean isDirty();
|
||||||
|
void markDirty();
|
||||||
|
void markClean();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param item
|
||||||
|
* @return all items belonging to passed class
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<StoredStack> findItems(@Nonnull Item item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param stack
|
||||||
|
* @return all items that match this itemstack
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
List<StoredStack> findItems(@Nonnull ItemStack stack);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
StoredStack getItem(@Nonnull UUID id);
|
||||||
|
|
||||||
|
int getStoredCount();
|
||||||
|
int getCapacity();
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
ItemStack insertItem(@Nonnull ItemStack item, boolean simulate);
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
ItemStack extractItem(@Nonnull UUID id, int amount, boolean simulate);
|
||||||
|
|
||||||
|
// not extending INBTSerializable to avoid serializing it as forgecaps
|
||||||
|
CompoundTag serializeNBT();
|
||||||
|
void deserializeNBT(CompoundTag nbt);
|
||||||
|
}
|
@ -0,0 +1,277 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.drive;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraftforge.registries.RegistryManager;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class MatteryDrive implements IMatteryDrive {
|
||||||
|
protected final HashMap<Item, List<StoredStack>> items = new HashMap<>();
|
||||||
|
protected final HashMap<UUID, StoredStack> items_uuid = new HashMap<>();
|
||||||
|
|
||||||
|
protected boolean dirty = false;
|
||||||
|
protected int different_stacks = 0;
|
||||||
|
protected int stored = 0;
|
||||||
|
protected int max_different_stacks;
|
||||||
|
protected int capacity;
|
||||||
|
|
||||||
|
public MatteryDrive(int capacity, int max_different_stacks) {
|
||||||
|
this.capacity = capacity;
|
||||||
|
this.max_different_stacks = max_different_stacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MatteryDrive(int capacity) {
|
||||||
|
this(capacity, 0xFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<StoredStack> getItems() {
|
||||||
|
int amount = 0;
|
||||||
|
|
||||||
|
for (var list : items.values()) {
|
||||||
|
amount += list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = new ArrayList<StoredStack>(amount);
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (var list : items.values()) {
|
||||||
|
for (var stack : list) {
|
||||||
|
output.set(i, stack);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDirty() {
|
||||||
|
return dirty;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDirty() {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markClean() {
|
||||||
|
dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStoredCount() {
|
||||||
|
return stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCapacity() {
|
||||||
|
return capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nonnull
|
||||||
|
public List<StoredStack> findItems(@Nonnull Item item) {
|
||||||
|
var list = items.get(item);
|
||||||
|
|
||||||
|
if (list != null)
|
||||||
|
return List.copyOf(list);
|
||||||
|
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nonnull
|
||||||
|
public List<StoredStack> findItems(@Nonnull ItemStack stack) {
|
||||||
|
var list = items.get(stack.getItem());
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
return List.of();
|
||||||
|
|
||||||
|
var amount = 0;
|
||||||
|
|
||||||
|
for (var _stack : list) {
|
||||||
|
if (ItemStack.tagMatches(_stack.stack(), stack)) {
|
||||||
|
amount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var build_list = new ArrayList<StoredStack>(amount);
|
||||||
|
|
||||||
|
for (var _stack : list) {
|
||||||
|
if (ItemStack.tagMatches(_stack.stack(), stack)) {
|
||||||
|
build_list.add(_stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return build_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public StoredStack getItem(@Nonnull UUID id) {
|
||||||
|
return items_uuid.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public ItemStack insertItem(@Nonnull ItemStack item, boolean simulate) {
|
||||||
|
int max_insert = Math.min(getCapacity() - getStoredCount(), item.getCount());
|
||||||
|
|
||||||
|
if (max_insert <= 0)
|
||||||
|
return item;
|
||||||
|
|
||||||
|
var listing = items.computeIfAbsent(item.getItem(), (key) -> new ArrayList<>());
|
||||||
|
|
||||||
|
for (var state : listing) {
|
||||||
|
if (ItemStack.tagMatches(state.stack(), item)) {
|
||||||
|
if (!simulate) {
|
||||||
|
state.stack().grow(max_insert);
|
||||||
|
stored += max_insert;
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
var copy_item = item.copy();
|
||||||
|
copy_item.shrink(max_insert);
|
||||||
|
return copy_item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (different_stacks >= max_different_stacks) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
different_stacks++;
|
||||||
|
var copy = item.copy();
|
||||||
|
copy.setCount(max_insert);
|
||||||
|
var state = new StoredStack(copy, UUID.randomUUID());
|
||||||
|
listing.add(state);
|
||||||
|
items_uuid.put(state.id(), state);
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
var copy_item = item.copy();
|
||||||
|
copy_item.shrink(max_insert);
|
||||||
|
return copy_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public ItemStack extractItem(@Nonnull UUID id, int amount, boolean simulate) {
|
||||||
|
var get = items_uuid.get(id);
|
||||||
|
|
||||||
|
if (get == null)
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
|
||||||
|
var extract = Math.min(amount, get.stack().getCount());
|
||||||
|
|
||||||
|
if (extract <= 0)
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
|
||||||
|
if (!simulate) {
|
||||||
|
if (extract == get.stack().getCount()) {
|
||||||
|
var listing = items.get(get.stack().getItem());
|
||||||
|
listing.remove(get);
|
||||||
|
}
|
||||||
|
|
||||||
|
get.stack().shrink(extract);
|
||||||
|
markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
var copy = get.stack().copy();
|
||||||
|
copy.setCount(extract);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag serializeNBT() {
|
||||||
|
final var compound = new CompoundTag();
|
||||||
|
|
||||||
|
compound.putInt("capacity", capacity);
|
||||||
|
compound.putInt("max_different_stacks", max_different_stacks);
|
||||||
|
|
||||||
|
var list_items = new ListTag();
|
||||||
|
compound.put("items", list_items);
|
||||||
|
|
||||||
|
for (var entry : items.entrySet()) {
|
||||||
|
var item = Objects.requireNonNull(entry.getKey().getRegistryName(), "Missing registry name for stored Item").toString();
|
||||||
|
var entry_compound = new CompoundTag();
|
||||||
|
|
||||||
|
entry_compound.putString("id", item);
|
||||||
|
var stack_list = new ListTag();
|
||||||
|
entry_compound.put("list", stack_list);
|
||||||
|
|
||||||
|
for (var stack : entry.getValue()) {
|
||||||
|
var stack_nbt = new CompoundTag();
|
||||||
|
stack_list.add(stack_nbt);
|
||||||
|
|
||||||
|
stack_nbt.putInt("count", stack.stack().getCount());
|
||||||
|
stack_nbt.putLongArray("uuid", new long[] { stack.id().getMostSignificantBits(), stack.id().getLeastSignificantBits() });
|
||||||
|
|
||||||
|
if (stack.stack().getTag() != null) {
|
||||||
|
stack_nbt.put("data", stack.stack().getTag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list_items.add(entry_compound);
|
||||||
|
}
|
||||||
|
|
||||||
|
return compound;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deserializeNBT(CompoundTag nbt) {
|
||||||
|
items.clear();
|
||||||
|
items_uuid.clear();
|
||||||
|
stored = 0;
|
||||||
|
different_stacks = 0;
|
||||||
|
|
||||||
|
capacity = nbt.getInt("capacity");
|
||||||
|
max_different_stacks = nbt.getInt("max_different_stacks");
|
||||||
|
|
||||||
|
var list_items = nbt.getList("items", Tag.TAG_COMPOUND);
|
||||||
|
var registry = RegistryManager.ACTIVE.getRegistry(Item.class);
|
||||||
|
|
||||||
|
for (var _entry : list_items) {
|
||||||
|
if (_entry instanceof CompoundTag entry) {
|
||||||
|
var item = registry.getValue(new ResourceLocation(entry.getString("id")));
|
||||||
|
|
||||||
|
if (item != null && item != Items.AIR) {
|
||||||
|
var map_list = items.computeIfAbsent(item, (key) -> new ArrayList<>());
|
||||||
|
|
||||||
|
var stack_list = entry.getList("list", Tag.TAG_COMPOUND);
|
||||||
|
|
||||||
|
for (var _stack : stack_list) {
|
||||||
|
if (_stack instanceof CompoundTag stack) {
|
||||||
|
var count = stack.getInt("count");
|
||||||
|
var _uuid = stack.getLongArray("uuid");
|
||||||
|
var data = stack.get("data");
|
||||||
|
|
||||||
|
var itemstack = new ItemStack(item, count);
|
||||||
|
|
||||||
|
if (data != null) {
|
||||||
|
itemstack.setTag((CompoundTag) data);
|
||||||
|
}
|
||||||
|
|
||||||
|
stored += count;
|
||||||
|
different_stacks += 1;
|
||||||
|
var state = new StoredStack(itemstack, new UUID(_uuid[0], _uuid[1]));
|
||||||
|
map_list.add(state);
|
||||||
|
items_uuid.put(state.id(), state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.capability.drive;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public record StoredStack(ItemStack stack, UUID id) {
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package ru.dbotthepony.mc.otm.item;
|
||||||
|
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
import net.minecraftforge.event.entity.player.EntityItemPickupEvent;
|
||||||
|
import net.minecraftforge.event.entity.player.PlayerEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
|
||||||
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||||
|
import ru.dbotthepony.mc.otm.capability.drive.DrivePool;
|
||||||
|
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive;
|
||||||
|
import ru.dbotthepony.mc.otm.capability.drive.MatteryDrive;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class ItemPortableCondensationDrive extends Item {
|
||||||
|
public ItemPortableCondensationDrive() {
|
||||||
|
super(new Properties().stacksTo(1).tab(OverdriveThatMatters.CREATIVE_TAB));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void onPickupEvent(EntityItemPickupEvent event) {
|
||||||
|
for (var stack : event.getPlayer().getInventory().items) {
|
||||||
|
if (stack.getItem() instanceof ItemPortableCondensationDrive) {
|
||||||
|
var cap = stack.getCapability(MatteryCapability.DRIVE).resolve().get();
|
||||||
|
|
||||||
|
var remaining = cap.insertItem(event.getItem().getItem(), false);
|
||||||
|
|
||||||
|
if (remaining.getCount() == event.getItem().getItem().getCount()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setCanceled(true);
|
||||||
|
|
||||||
|
if (remaining.getCount() > 0) {
|
||||||
|
event.getItem().getItem().setCount(remaining.getCount());
|
||||||
|
} else {
|
||||||
|
event.getItem().discard();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DriveCapability implements ICapabilityProvider {
|
||||||
|
private UUID uuid;
|
||||||
|
private final ItemStack stack;
|
||||||
|
private final LazyOptional<IMatteryDrive> resolver = LazyOptional.of(() -> {
|
||||||
|
return DrivePool.get(uuid, (tag) -> {
|
||||||
|
var drive = new MatteryDrive(4000);
|
||||||
|
drive.deserializeNBT(tag);
|
||||||
|
return drive;
|
||||||
|
}, () -> new MatteryDrive(4000));
|
||||||
|
});
|
||||||
|
|
||||||
|
DriveCapability(ItemStack stack) {
|
||||||
|
this.stack = stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
|
||||||
|
if (cap == MatteryCapability.DRIVE) {
|
||||||
|
if (uuid == null) {
|
||||||
|
var get_value = stack.getOrCreateTag().getLongArray("uuid");
|
||||||
|
|
||||||
|
if (get_value.length == 2) {
|
||||||
|
uuid = new UUID(get_value[0], get_value[1]);
|
||||||
|
} else {
|
||||||
|
uuid = UUID.randomUUID();
|
||||||
|
stack.getOrCreateTag().putLongArray("uuid", new long[] { uuid.getMostSignificantBits(), uuid.getLeastSignificantBits() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolver.cast();
|
||||||
|
}
|
||||||
|
|
||||||
|
return LazyOptional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) {
|
||||||
|
return new DriveCapability(stack);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
# Make Screen.title be public and easily modifiable
|
# Make Screen.title be public and easily modifiable
|
||||||
public-f net.minecraft.client.gui.screens.Screen f_96539_ # title
|
public-f net.minecraft.client.gui.screens.Screen f_96539_ # title
|
||||||
|
public net.minecraft.server.MinecraftServer f_129744_ # storageSource
|
||||||
|
|
||||||
# for accessing and setting from MatteryScreen class
|
# for accessing and setting from MatteryScreen class
|
||||||
# protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_97724_ # lastQuickMoved
|
# protected net.minecraft.client.gui.screens.inventory.AbstractContainerScreen f_97724_ # lastQuickMoved
|
||||||
|
Loading…
Reference in New Issue
Block a user