Skip to content

Commit

Permalink
fold in onedatastore
Browse files Browse the repository at this point in the history
  • Loading branch information
BasiqueEvangelist committed Aug 6, 2024
1 parent 24dd8a3 commit ba9c260
Show file tree
Hide file tree
Showing 15 changed files with 520 additions and 9 deletions.
7 changes: 0 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,6 @@ repositories {
}
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
mavenCentral()
maven {
name = "Basique's Maven"
url = 'https://basique.top/maven'
}
}

dependencies {
Expand All @@ -53,9 +49,6 @@ dependencies {
modImplementation include('me.lucko:fabric-permissions-api:0.2-SNAPSHOT')
shadow implementation("blue.endless:jankson:1.2.0")

modImplementation include("me.basiqueevangelist:onedatastore:${project.onedatastore_version}") {
transitive = false
}
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/me/basiqueevangelist/onedatastore/api/Component.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package me.basiqueevangelist.onedatastore.api;

import me.basiqueevangelist.onedatastore.impl.OneDataStoreInit;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.ApiStatus;

import java.util.function.Function;

@ApiStatus.NonExtendable
public interface Component<T extends ComponentInstance, C> {
Identifier id();

Function<C, T> factory();

static <T extends ComponentInstance> Component<T, PlayerDataEntry> registerPlayer(Identifier id, Function<PlayerDataEntry, T> factory) {
return OneDataStoreInit.registerPlayerComponent(id, factory);
}

static <T extends ComponentInstance> Component<T, DataStore> registerGlobal(Identifier id, Function<DataStore, T> factory) {
return OneDataStoreInit.registerGlobalComponent(id, factory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package me.basiqueevangelist.onedatastore.api;

import net.minecraft.nbt.NbtCompound;

public interface ComponentInstance {
/**
* Called component load if the component isn't present in the data entry.
* Can be used to run data migration.
*/
default void wasMissing() {

}

void fromTag(NbtCompound tag);

NbtCompound toTag(NbtCompound tag);
}
24 changes: 24 additions & 0 deletions src/main/java/me/basiqueevangelist/onedatastore/api/DataStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package me.basiqueevangelist.onedatastore.api;

import me.basiqueevangelist.onedatastore.impl.OneDataStoreState;
import net.minecraft.server.MinecraftServer;
import org.jetbrains.annotations.ApiStatus;

import java.util.Collection;
import java.util.Map;
import java.util.UUID;

@ApiStatus.NonExtendable
public interface DataStore {
static DataStore getFor(MinecraftServer server) {
return OneDataStoreState.getFrom(server);
}

PlayerDataEntry getPlayerEntry(UUID playerId);

<T extends ComponentInstance> T get(Component<T, DataStore> component);

<T extends ComponentInstance> T getPlayer(UUID playerId, Component<T, PlayerDataEntry> component);

Collection<PlayerDataEntry> players();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.basiqueevangelist.onedatastore.api;

import org.jetbrains.annotations.ApiStatus;

import java.util.UUID;

@ApiStatus.NonExtendable
public interface PlayerDataEntry {
UUID playerId();

DataStore dataStore();

<T extends ComponentInstance> T get(Component<T, PlayerDataEntry> component);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package me.basiqueevangelist.onedatastore.impl;

import me.basiqueevangelist.onedatastore.api.Component;
import me.basiqueevangelist.onedatastore.api.ComponentInstance;
import net.minecraft.util.Identifier;

import java.util.function.Function;

public record ComponentImpl<T extends ComponentInstance, C> (
Identifier id,
Function<C, T> factory
) implements Component<T, C> {
@Override
public String toString() {
return "ComponentImpl{" +
"id=" + id +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package me.basiqueevangelist.onedatastore.impl;

import me.basiqueevangelist.onedatastore.api.Component;
import me.basiqueevangelist.onedatastore.api.ComponentInstance;
import me.basiqueevangelist.onedatastore.api.DataStore;
import me.basiqueevangelist.onedatastore.api.PlayerDataEntry;
import me.basiqueevangelist.onedatastore.impl.command.PurgeCommand;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.util.Identifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

public class OneDataStoreInit implements ModInitializer {
static final Logger LOGGER = LoggerFactory.getLogger("OneDataStore");

public static final Map<Identifier, Component<?, PlayerDataEntry>> PLAYER_COMPONENTS = new HashMap<>();
public static final Map<Identifier, Component<?, DataStore>> GLOBAL_COMPONENTS = new HashMap<>();

@Override
public void onInitialize() {
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
OneDataStoreState.getFrom(server).getPlayerEntry(handler.player.getUuid());
});

CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
PurgeCommand.register(dispatcher);
});
}

public static <T extends ComponentInstance> Component<T, PlayerDataEntry> registerPlayerComponent(Identifier id, Function<PlayerDataEntry, T> factory) {
ComponentImpl<T, PlayerDataEntry> component = new ComponentImpl<>(id, factory);
PLAYER_COMPONENTS.put(id, component);
return component;
}

public static <T extends ComponentInstance> Component<T, DataStore> registerGlobalComponent(Identifier id, Function<DataStore, T> factory) {
ComponentImpl<T, DataStore> component = new ComponentImpl<>(id, factory);
GLOBAL_COMPONENTS.put(id, component);
return component;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package me.basiqueevangelist.onedatastore.impl;

import me.basiqueevangelist.onedatastore.api.Component;
import me.basiqueevangelist.onedatastore.api.ComponentInstance;
import me.basiqueevangelist.onedatastore.api.DataStore;
import me.basiqueevangelist.onedatastore.api.PlayerDataEntry;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.PersistentState;

import java.util.*;

public class OneDataStoreState extends PersistentState implements DataStore {
private final Map<UUID, PlayerDataEntryImpl> players = new HashMap<>();
private final Map<Component<?, DataStore>, ComponentInstance> components = new HashMap<>();
private static final ReentrantLoadProtector SAFEGUARD = new ReentrantLoadProtector(() -> new IllegalStateException("Tried to recursively load OneDataStore state!"));

public static OneDataStoreState getFrom(MinecraftServer server) {
try (var scope = SAFEGUARD.enter()) {
return server.getOverworld().getPersistentStateManager().getOrCreate(
OneDataStoreState::new,
OneDataStoreState::new,
"onedatastore"
);
}
}

private OneDataStoreState() {
for (Component<?, DataStore> comp : OneDataStoreInit.GLOBAL_COMPONENTS.values()) {
ComponentInstance inst = comp.factory().apply(this);
inst.wasMissing();
components.put(comp, inst);
}
}

private OneDataStoreState(NbtCompound tag) {
var playersTag = tag.getList("Players", NbtElement.COMPOUND_TYPE);
for (int i = 0; i < playersTag.size(); i++) {
var playerTag = playersTag.getCompound(i);

UUID playerId = playerTag.getUuid("UUID");

players.put(playerId, new PlayerDataEntryImpl(this, playerId));
}

for (Component<?, DataStore> comp : OneDataStoreInit.GLOBAL_COMPONENTS.values()) {
components.put(comp, comp.factory().apply(this));
}

for (int i = 0; i < playersTag.size(); i++) {
var playerTag = playersTag.getCompound(i);

UUID playerId = playerTag.getUuid("UUID");

players.get(playerId).fromTag(playerTag);
}

for (Map.Entry<Component<?, DataStore>, ComponentInstance> entry : components.entrySet()) {
var tagName = entry.getKey().id().toString();

if (tag.contains(tagName, NbtElement.COMPOUND_TYPE)) {
try {
entry.getValue().fromTag(tag.getCompound(tagName));
} catch (Exception e) {
OneDataStoreInit.LOGGER.error("Encountered error while deserializing {}", tagName, e);
}
} else {
entry.getValue().wasMissing();
}
}
}

@Override
public PlayerDataEntry getPlayerEntry(UUID playerId) {
return players.computeIfAbsent(playerId, id -> {
var entry = new PlayerDataEntryImpl(this, id);
entry.wasMissing();
return entry;
});
}

@Override
@SuppressWarnings("unchecked")
public <T extends ComponentInstance> T get(Component<T, DataStore> component) {
return (T) components.get(component);
}

@Override
public <T extends ComponentInstance> T getPlayer(UUID playerId, Component<T, PlayerDataEntry> component) {
return getPlayerEntry(playerId).get(component);
}

@Override
public Collection<PlayerDataEntry> players() {
return Collections.unmodifiableCollection(players.values());
}

public Map<UUID, PlayerDataEntryImpl> playersMap() {
return players;
}

public void reinitComponent(Component<?, DataStore> component) {
components.remove(component);
ComponentInstance inst = component.factory().apply(this);
inst.wasMissing();
components.put(component, inst);
}

@Override
public NbtCompound writeNbt(NbtCompound tag) {
var playersTag = new NbtList();
tag.put("Players", playersTag);

for (var entry : players.entrySet()) {
playersTag.add(entry.getValue().toTag(new NbtCompound()));
}

for (Map.Entry<Component<?, DataStore>, ComponentInstance> entry : components.entrySet()) {
var tagName = entry.getKey().id().toString();

try {
tag.put(tagName, entry.getValue().toTag(new NbtCompound()));
} catch (Exception e) {
OneDataStoreInit.LOGGER.error("Encountered error while serializing {}", tagName, e);
}
}

return tag;
}

@Override
public boolean isDirty() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package me.basiqueevangelist.onedatastore.impl;

import me.basiqueevangelist.onedatastore.api.Component;
import me.basiqueevangelist.onedatastore.api.ComponentInstance;
import me.basiqueevangelist.onedatastore.api.PlayerDataEntry;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtHelper;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class PlayerDataEntryImpl implements PlayerDataEntry {
private final UUID playerId;
private final OneDataStoreState state;
private final Map<Component<?, PlayerDataEntry>, ComponentInstance> components = new HashMap<>();

public PlayerDataEntryImpl(OneDataStoreState state, UUID playerId) {
this.playerId = playerId;
this.state = state;

for (Component<?, PlayerDataEntry> comp : OneDataStoreInit.PLAYER_COMPONENTS.values()) {
components.put(comp, comp.factory().apply(this));
}
}

public void fromTag(NbtCompound tag) {
for (Map.Entry<Component<?, PlayerDataEntry>, ComponentInstance> entry : components.entrySet()) {
var tagName = entry.getKey().id().toString();

if (tag.contains(tagName, NbtElement.COMPOUND_TYPE)) {
try {
entry.getValue().fromTag(tag.getCompound(tagName));
} catch (Exception e) {
OneDataStoreInit.LOGGER.error("Encountered error while deserializing {} for {}", tagName, playerId, e);
}
} else {
entry.getValue().wasMissing();
}
}
}

public void wasMissing() {
for (Map.Entry<Component<?, PlayerDataEntry>, ComponentInstance> entry : components.entrySet()) {
entry.getValue().wasMissing();
}
}

@Override
public UUID playerId() {
return playerId;
}

@Override
public OneDataStoreState dataStore() {
return state;
}

@Override
@SuppressWarnings("unchecked")
public <T extends ComponentInstance> T get(Component<T, PlayerDataEntry> component) {
return (T) components.get(component);
}

public NbtCompound toTag(NbtCompound tag) {
tag.put("UUID", NbtHelper.fromUuid(playerId));

for (Map.Entry<Component<?, PlayerDataEntry>, ComponentInstance> entry : components.entrySet()) {
var tagName = entry.getKey().id().toString();

try {
tag.put(tagName, entry.getValue().toTag(new NbtCompound()));
} catch (Exception e) {
OneDataStoreInit.LOGGER.error("Encountered error while serializing {} for {}", tagName, playerId, e);
}
}

return tag;
}

@Override
public String toString() {
return "PlayerDataEntryImpl{" +
"playerId=" + playerId +
'}';
}
}
Loading

0 comments on commit ba9c260

Please sign in to comment.