hytale-server/com/hypixel/hytale/builtin/adventure/memories/MemoriesPlugin.java

354 lines
15 KiB
Java

package com.hypixel.hytale.builtin.adventure.memories;
import com.hypixel.hytale.builtin.adventure.memories.commands.MemoriesCommand;
import com.hypixel.hytale.builtin.adventure.memories.component.PlayerMemories;
import com.hypixel.hytale.builtin.adventure.memories.interactions.MemoriesConditionInteraction;
import com.hypixel.hytale.builtin.adventure.memories.interactions.SetMemoriesCapacityInteraction;
import com.hypixel.hytale.builtin.adventure.memories.memories.Memory;
import com.hypixel.hytale.builtin.adventure.memories.memories.MemoryProvider;
import com.hypixel.hytale.builtin.adventure.memories.memories.npc.NPCMemory;
import com.hypixel.hytale.builtin.adventure.memories.memories.npc.NPCMemoryProvider;
import com.hypixel.hytale.builtin.adventure.memories.page.MemoriesPage;
import com.hypixel.hytale.builtin.adventure.memories.page.MemoriesPageSupplier;
import com.hypixel.hytale.builtin.adventure.memories.temple.ForgottenTempleConfig;
import com.hypixel.hytale.builtin.adventure.memories.temple.TempleRespawnPlayersSystem;
import com.hypixel.hytale.builtin.adventure.memories.window.MemoriesWindow;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
import com.hypixel.hytale.codec.codecs.map.Object2DoubleMapCodec;
import com.hypixel.hytale.codec.util.RawJsonReader;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentRegistryProxy;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.dependency.Dependency;
import com.hypixel.hytale.component.dependency.Order;
import com.hypixel.hytale.component.dependency.SystemDependency;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.RefSystem;
import com.hypixel.hytale.protocol.packets.player.UpdateMemoriesFeatureStatus;
import com.hypixel.hytale.protocol.packets.window.WindowType;
import com.hypixel.hytale.server.core.Constants;
import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig;
import com.hypixel.hytale.server.core.entity.entities.Player;
import com.hypixel.hytale.server.core.entity.entities.player.windows.Window;
import com.hypixel.hytale.server.core.io.PacketHandler;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSystems;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.core.util.BsonUtil;
import com.hypixel.hytale.server.core.util.Config;
import com.hypixel.hytale.server.npc.AllNPCsLoadedEvent;
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
import it.unimi.dsi.fastutil.objects.Object2DoubleMaps;
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class MemoriesPlugin extends JavaPlugin {
private static MemoriesPlugin instance;
private final Config<MemoriesPlugin.MemoriesPluginConfig> config = this.withConfig(MemoriesPlugin.MemoriesPluginConfig.CODEC);
private final List<MemoryProvider<?>> providers = new ObjectArrayList();
private final Map<String, Set<Memory>> allMemories = new Object2ObjectOpenHashMap();
private ComponentType<EntityStore, PlayerMemories> playerMemoriesComponentType;
@Nullable
private MemoriesPlugin.RecordedMemories recordedMemories;
private boolean hasInitializedMemories;
public static MemoriesPlugin get() {
return instance;
}
public MemoriesPlugin(@Nonnull JavaPluginInit init) {
super(init);
instance = this;
}
@Override
protected void setup() {
ComponentRegistryProxy<EntityStore> entityStoreRegistry = this.getEntityStoreRegistry();
this.getCommandRegistry().registerCommand(new MemoriesCommand());
OpenCustomUIInteraction.registerCustomPageSupplier(this, MemoriesPage.class, "Memories", new MemoriesPageSupplier());
Window.CLIENT_REQUESTABLE_WINDOW_TYPES.put(WindowType.Memories, MemoriesWindow::new);
this.playerMemoriesComponentType = entityStoreRegistry.registerComponent(PlayerMemories.class, "PlayerMemories", PlayerMemories.CODEC);
NPCMemoryProvider npcMemoryProvider = new NPCMemoryProvider();
this.registerMemoryProvider(npcMemoryProvider);
entityStoreRegistry.registerSystem(new NPCMemory.GatherMemoriesSystem(npcMemoryProvider.getCollectionRadius()));
for (MemoryProvider<?> provider : this.providers) {
BuilderCodec<? extends Memory> codec = (BuilderCodec<? extends Memory>)provider.getCodec();
this.getCodecRegistry(Memory.CODEC).register(provider.getId(), codec.getInnerClass(), codec);
}
this.getEventRegistry().register(AllNPCsLoadedEvent.class, event -> this.onAssetsLoad());
entityStoreRegistry.registerSystem(new MemoriesPlugin.PlayerAddedSystem());
this.getCodecRegistry(Interaction.CODEC).register("SetMemoriesCapacity", SetMemoriesCapacityInteraction.class, SetMemoriesCapacityInteraction.CODEC);
this.getCodecRegistry(GameplayConfig.PLUGIN_CODEC).register(MemoriesGameplayConfig.class, "Memories", MemoriesGameplayConfig.CODEC);
this.getCodecRegistry(Interaction.CODEC).register("MemoriesCondition", MemoriesConditionInteraction.class, MemoriesConditionInteraction.CODEC);
entityStoreRegistry.registerSystem(new TempleRespawnPlayersSystem());
this.getCodecRegistry(GameplayConfig.PLUGIN_CODEC).register(ForgottenTempleConfig.class, "ForgottenTemple", ForgottenTempleConfig.CODEC);
}
@Override
protected void start() {
try {
Path path = Constants.UNIVERSE_PATH.resolve("memories.json");
if (Files.exists(path)) {
this.recordedMemories = RawJsonReader.readSync(path, MemoriesPlugin.RecordedMemories.CODEC, this.getLogger());
} else {
this.recordedMemories = new MemoriesPlugin.RecordedMemories();
}
} catch (IOException var2) {
throw new RuntimeException(var2);
}
this.hasInitializedMemories = true;
this.onAssetsLoad();
}
@Override
protected void shutdown() {
this.recordedMemories.lock.readLock().lock();
try {
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
} catch (IOException var5) {
throw new RuntimeException(var5);
} finally {
this.recordedMemories.lock.readLock().unlock();
}
}
private void onAssetsLoad() {
if (this.hasInitializedMemories) {
this.allMemories.clear();
for (MemoryProvider<?> provider : this.providers) {
for (Entry<String, Set<Memory>> entry : provider.getAllMemories().entrySet()) {
this.allMemories.computeIfAbsent(entry.getKey(), k -> new HashSet<>()).addAll(entry.getValue());
}
}
}
}
public MemoriesPlugin.MemoriesPluginConfig getConfig() {
return this.config.get();
}
public ComponentType<EntityStore, PlayerMemories> getPlayerMemoriesComponentType() {
return this.playerMemoriesComponentType;
}
public <T extends Memory> void registerMemoryProvider(MemoryProvider<T> memoryProvider) {
this.providers.add(memoryProvider);
}
public Map<String, Set<Memory>> getAllMemories() {
return this.allMemories;
}
public int getMemoriesLevel(@Nonnull GameplayConfig gameplayConfig) {
MemoriesGameplayConfig config = MemoriesGameplayConfig.get(gameplayConfig);
int memoriesLevel = 1;
if (config == null) {
return memoriesLevel;
} else {
int recordedMemoriesCount = this.getRecordedMemories().size();
int[] memoriesAmountPerLevel = config.getMemoriesAmountPerLevel();
for (int i = 0; i < memoriesAmountPerLevel.length && recordedMemoriesCount >= memoriesAmountPerLevel[i]; i++) {
memoriesLevel += i + 1;
}
return memoriesLevel;
}
}
public int getMemoriesForNextLevel(@Nonnull GameplayConfig gameplayConfig) {
MemoriesGameplayConfig memoriesConfig = MemoriesGameplayConfig.get(gameplayConfig);
if (memoriesConfig == null) {
return -1;
} else {
int memoriesLevel = this.getMemoriesLevel(gameplayConfig);
int[] memoriesAmountPerLevel = memoriesConfig.getMemoriesAmountPerLevel();
if (memoriesLevel >= memoriesAmountPerLevel.length) {
return -1;
} else {
int recordedMemoriesCount = this.getRecordedMemories().size();
return memoriesAmountPerLevel[memoriesLevel] - recordedMemoriesCount;
}
}
}
public boolean hasRecordedMemory(Memory memory) {
this.recordedMemories.lock.readLock().lock();
boolean var2;
try {
var2 = this.recordedMemories.memories.contains(memory);
} finally {
this.recordedMemories.lock.readLock().unlock();
}
return var2;
}
public boolean recordPlayerMemories(@Nonnull PlayerMemories playerMemories) {
this.recordedMemories.lock.writeLock().lock();
try {
if (playerMemories.takeMemories(this.recordedMemories.memories)) {
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
return true;
}
} catch (IOException var6) {
throw new RuntimeException(var6);
} finally {
this.recordedMemories.lock.writeLock().unlock();
}
return false;
}
@Nonnull
public Set<Memory> getRecordedMemories() {
this.recordedMemories.lock.readLock().lock();
HashSet var1;
try {
var1 = new HashSet<>(this.recordedMemories.memories);
} finally {
this.recordedMemories.lock.readLock().unlock();
}
return var1;
}
public void clearRecordedMemories() {
this.recordedMemories.lock.writeLock().lock();
try {
this.recordedMemories.memories.clear();
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
} catch (IOException var5) {
throw new RuntimeException(var5);
} finally {
this.recordedMemories.lock.writeLock().unlock();
}
}
public void recordAllMemories() {
this.recordedMemories.lock.writeLock().lock();
try {
for (Entry<String, Set<Memory>> entry : this.allMemories.entrySet()) {
this.recordedMemories.memories.addAll(entry.getValue());
}
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
} catch (IOException var6) {
throw new RuntimeException(var6);
} finally {
this.recordedMemories.lock.writeLock().unlock();
}
}
public static class MemoriesPluginConfig {
public static final BuilderCodec<MemoriesPlugin.MemoriesPluginConfig> CODEC = BuilderCodec.builder(
MemoriesPlugin.MemoriesPluginConfig.class, MemoriesPlugin.MemoriesPluginConfig::new
)
.append(
new KeyedCodec<>("CollectionRadius", new Object2DoubleMapCodec<>(Codec.STRING, Object2DoubleOpenHashMap::new)),
(config, map) -> config.collectionRadius = map,
config -> config.collectionRadius
)
.add()
.build();
private Object2DoubleMap<String> collectionRadius;
@Nonnull
public Object2DoubleMap<String> getCollectionRadius() {
return (Object2DoubleMap<String>)(this.collectionRadius != null ? this.collectionRadius : Object2DoubleMaps.EMPTY_MAP);
}
}
public static class PlayerAddedSystem extends RefSystem<EntityStore> {
@Nonnull
private final Set<Dependency<EntityStore>> dependencies = Set.of(new SystemDependency<>(Order.AFTER, PlayerSystems.PlayerSpawnedSystem.class));
@Nonnull
private final Query<EntityStore> query = Query.and(Player.getComponentType(), PlayerRef.getComponentType());
@Nonnull
@Override
public Query<EntityStore> getQuery() {
return this.query;
}
@Nonnull
@Override
public Set<Dependency<EntityStore>> getDependencies() {
return this.dependencies;
}
@Override
public void onEntityAdded(
@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
) {
Player playerComponent = store.getComponent(ref, Player.getComponentType());
assert playerComponent != null;
PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType());
assert playerRefComponent != null;
PlayerMemories playerMemoriesComponent = store.getComponent(ref, PlayerMemories.getComponentType());
boolean isFeatureUnlockedByPlayer = playerMemoriesComponent != null;
PacketHandler playerConnection = playerRefComponent.getPacketHandler();
playerConnection.writeNoCache(new UpdateMemoriesFeatureStatus(isFeatureUnlockedByPlayer));
}
@Override
public void onEntityRemove(
@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
) {
}
}
private static class RecordedMemories {
public static final BuilderCodec<MemoriesPlugin.RecordedMemories> CODEC = BuilderCodec.builder(
MemoriesPlugin.RecordedMemories.class, MemoriesPlugin.RecordedMemories::new
)
.append(new KeyedCodec<>("Memories", new ArrayCodec<>(Memory.CODEC, Memory[]::new)), (recordedMemories, memories) -> {
if (memories != null) {
Collections.addAll(recordedMemories.memories, memories);
}
}, recordedMemories -> recordedMemories.memories.toArray(Memory[]::new))
.add()
.build();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Set<Memory> memories = new HashSet<>();
}
}