Skip to content

Commit 73a3bbc

Browse files
authored
Add map template processors (#335)
* Refactor Plasmid registries * Fixes around registries * Tweaks * Fix binary incompatibility * Add template processors * Sort registry entries * Use custom context parameters for map template processors * Wrap codec * Rename registry * Fix TestGame
1 parent 807aa61 commit 73a3bbc

File tree

11 files changed

+395
-24
lines changed

11 files changed

+395
-24
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package xyz.nucleoid.plasmid.api.map;
2+
3+
import net.minecraft.util.context.ContextParameter;
4+
import xyz.nucleoid.plasmid.api.game.common.team.GameTeamList;
5+
import xyz.nucleoid.plasmid.impl.Plasmid;
6+
7+
public class MapLoadContexts {
8+
public static final ContextParameter<GameTeamList> TEAM_LIST = of("team_list");
9+
10+
public static <T> ContextParameter<T> of(String key) {
11+
return new ContextParameter<>(Plasmid.id(key));
12+
}
13+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package xyz.nucleoid.plasmid.api.map.template.processor;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import net.minecraft.util.context.ContextParameterMap;
6+
import xyz.nucleoid.map_templates.MapTemplate;
7+
import xyz.nucleoid.plasmid.api.game.GameActivity;
8+
import xyz.nucleoid.plasmid.api.registry.PlasmidRegistries;
9+
10+
import java.util.function.Function;
11+
12+
/**
13+
* Modifies a {@link MapTemplate}. It must be used in the context of a {@link GameActivity}.
14+
*
15+
* @author Hugman
16+
* @see MapTemplateProcessorTypes
17+
*/
18+
public interface MapTemplateProcessor {
19+
Codec<MapTemplateProcessor> CODEC = PlasmidRegistries.MAP_TEMPLATE_PROCESSOR_TYPE.getCodec().dispatch(MapTemplateProcessor::getCodec, Function.identity());
20+
21+
void processTemplate(MapTemplate template, ContextParameterMap.Builder parameters);
22+
23+
MapCodec<? extends MapTemplateProcessor> getCodec();
24+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package xyz.nucleoid.plasmid.api.map.template.processor;
2+
3+
import com.mojang.serialization.MapCodec;
4+
import net.minecraft.registry.Registry;
5+
import net.minecraft.util.Identifier;
6+
import xyz.nucleoid.plasmid.api.registry.PlasmidRegistries;
7+
import xyz.nucleoid.plasmid.impl.Plasmid;
8+
9+
public class MapTemplateProcessorTypes {
10+
public static final MapCodec<ReplaceBlocksTemplateProcessor> REPLACE_BLOCKS = of("replace_blocks", ReplaceBlocksTemplateProcessor.CODEC);
11+
public static final MapCodec<ReplaceBlockEntitiesTemplateProcessor> REPLACE_BLOCK_ENTITIES = of("replace_block_entities", ReplaceBlockEntitiesTemplateProcessor.CODEC);
12+
public static final MapCodec<TeamColorMapTemplateProcessor> TEAM_COLORS = of("team_colors", TeamColorMapTemplateProcessor.CODEC);
13+
14+
private static <T extends MapTemplateProcessor> MapCodec<T> of(String name, MapCodec<T> codec) {
15+
return of(Identifier.of(Plasmid.ID, name), codec);
16+
}
17+
18+
public static <T extends MapTemplateProcessor> MapCodec<T> of(Identifier identifier, MapCodec<T> codec) {
19+
return Registry.register(PlasmidRegistries.MAP_TEMPLATE_PROCESSOR_TYPE, identifier, codec);
20+
}
21+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package xyz.nucleoid.plasmid.api.map.template.processor;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import net.minecraft.nbt.NbtCompound;
6+
import net.minecraft.util.context.ContextParameterMap;
7+
import xyz.nucleoid.map_templates.MapTemplate;
8+
9+
import java.util.Map;
10+
11+
/**
12+
* Template processor that replaces block entity NBT data in a {@link MapTemplate} based on a simple search and replace map.
13+
*
14+
* @param searchAndReplace the map of keys to replace with their corresponding values
15+
*
16+
* @author Hugman
17+
*/
18+
public record ReplaceBlockEntitiesTemplateProcessor(Map<String, String> searchAndReplace) implements MapTemplateProcessor {
19+
public static final MapCodec<ReplaceBlockEntitiesTemplateProcessor> CODEC = Codec.unboundedMap(Codec.STRING, Codec.STRING).fieldOf("search_and_replace").xmap(ReplaceBlockEntitiesTemplateProcessor::new, ReplaceBlockEntitiesTemplateProcessor::searchAndReplace);
20+
21+
@Override
22+
public MapCodec<? extends MapTemplateProcessor> getCodec() {
23+
return CODEC;
24+
}
25+
26+
@Override
27+
public void processTemplate(MapTemplate template, ContextParameterMap.Builder parameters) {
28+
template.getBounds().forEach(pos -> {
29+
var nbtCompound = template.getBlockEntityNbt(pos);
30+
if (nbtCompound instanceof NbtCompound) {
31+
if (searchAndReplace(nbtCompound, false)) {
32+
template.setBlockEntityNbt(pos, nbtCompound);
33+
}
34+
}
35+
});
36+
}
37+
38+
private boolean searchAndReplace(NbtCompound compound, boolean hasChanged) {
39+
for (var key : compound.getKeys()) {
40+
var stringValue = compound.getString(key);
41+
if (stringValue.isPresent()) {
42+
var val = stringValue.get();
43+
for (var entry : searchAndReplace.entrySet()) {
44+
if (val.equals(entry.getKey())) {
45+
compound.putString(key, entry.getValue());
46+
hasChanged = true;
47+
break;
48+
}
49+
}
50+
}
51+
var compoundValue = compound.getCompound(key);
52+
if (compoundValue.isPresent()) {
53+
var val = compoundValue.get();
54+
if (searchAndReplace(val, false)) {
55+
hasChanged = true;
56+
break;
57+
}
58+
}
59+
var listValue = compound.getList(key);
60+
if (listValue.isPresent()) {
61+
var list = listValue.get();
62+
for (var i = 0; i < list.size(); i++) {
63+
var item = list.get(i);
64+
if (item instanceof NbtCompound itemCompound) {
65+
if (searchAndReplace(itemCompound, false)) {
66+
list.set(i, itemCompound);
67+
hasChanged = true;
68+
}
69+
}
70+
}
71+
}
72+
}
73+
return hasChanged;
74+
}
75+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package xyz.nucleoid.plasmid.api.map.template.processor;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.MapCodec;
5+
import net.minecraft.block.Block;
6+
import net.minecraft.block.BlockState;
7+
import net.minecraft.registry.Registries;
8+
import net.minecraft.state.property.Property;
9+
import net.minecraft.util.context.ContextParameterMap;
10+
import xyz.nucleoid.map_templates.MapTemplate;
11+
12+
import java.util.Map;
13+
14+
/**
15+
* Template processor that replaces blocks in a template with specified blocks.
16+
*
17+
* @param blocks a map of blocks to replace, where the key is the block to be replaced and the value is the new block
18+
*
19+
* @author Hugman
20+
*/
21+
public record ReplaceBlocksTemplateProcessor(Map<Block, Block> blocks) implements MapTemplateProcessor {
22+
public static final MapCodec<ReplaceBlocksTemplateProcessor> CODEC = Codec.unboundedMap(Registries.BLOCK.getCodec(), Registries.BLOCK.getCodec()).fieldOf("blocks").xmap(ReplaceBlocksTemplateProcessor::new, ReplaceBlocksTemplateProcessor::blocks);
23+
24+
@Override
25+
public MapCodec<? extends MapTemplateProcessor> getCodec() {
26+
return CODEC;
27+
}
28+
29+
@Override
30+
public void processTemplate(MapTemplate template, ContextParameterMap.Builder parameters) {
31+
template.getBounds().forEach(pos -> {
32+
var state = template.getBlockState(pos);
33+
var block = state.getBlock();
34+
Block newBlock = null;
35+
for (var entry : this.blocks.entrySet()) {
36+
if (entry.getKey() == block) {
37+
newBlock = entry.getValue();
38+
break;
39+
}
40+
}
41+
if (newBlock != null) {
42+
BlockState newState = newBlock.getDefaultState();
43+
for (Property property : state.getProperties()) {
44+
newState = newState.contains(property) ? newState.with(property, state.get(property)) : newState;
45+
}
46+
template.setBlockState(pos, newState);
47+
}
48+
});
49+
}
50+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package xyz.nucleoid.plasmid.api.map.template.processor;
2+
3+
import com.mojang.serialization.MapCodec;
4+
import net.minecraft.block.Block;
5+
import net.minecraft.registry.Registries;
6+
import net.minecraft.text.Text;
7+
import net.minecraft.util.DyeColor;
8+
import net.minecraft.util.context.ContextParameterMap;
9+
import net.minecraft.util.context.ContextType;
10+
import xyz.nucleoid.map_templates.MapTemplate;
11+
import xyz.nucleoid.plasmid.api.game.GameOpenException;
12+
import xyz.nucleoid.plasmid.api.map.MapLoadContexts;
13+
import xyz.nucleoid.plasmid.api.util.ColoredBlocks;
14+
import xyz.nucleoid.plasmid.api.util.ColoredItems;
15+
16+
import java.util.HashMap;
17+
import java.util.List;
18+
19+
/**
20+
* Template processor that recolors blocks and items found in block entities (by their ID) in a template with team-specific colors.
21+
*
22+
* <p><strong>This processor requires {@link MapLoadContexts#TEAM_LIST} in the parameters before loading the processors.</strong>
23+
*
24+
* @param baseColors the colors to recolor. Each entry must correspond to a team of the loaded game.
25+
* @author Hugman
26+
* @see ColoredBlocks
27+
* @see ColoredItems
28+
* @see ReplaceBlocksTemplateProcessor
29+
* @see ReplaceBlockEntitiesTemplateProcessor
30+
*/
31+
public record TeamColorMapTemplateProcessor(List<DyeColor> baseColors) implements MapTemplateProcessor {
32+
public static final MapCodec<TeamColorMapTemplateProcessor> CODEC = DyeColor.CODEC.listOf().fieldOf("base_colors").xmap(TeamColorMapTemplateProcessor::new, TeamColorMapTemplateProcessor::baseColors);
33+
34+
private static final ContextType CONTEXT_TYPE = new ContextType.Builder().require(MapLoadContexts.TEAM_LIST).build();
35+
36+
@Override
37+
public MapCodec<? extends MapTemplateProcessor> getCodec() {
38+
return CODEC;
39+
}
40+
41+
@Override
42+
public void processTemplate(MapTemplate template, ContextParameterMap.Builder parameters) {
43+
parameters.build(CONTEXT_TYPE);
44+
var teamList = parameters.getOrThrow(MapLoadContexts.TEAM_LIST).list();
45+
46+
if (teamList.size() > this.baseColors.size()) {
47+
throw new GameOpenException(Text.literal("Not enough base colors provided for the number of teams."));
48+
}
49+
50+
var blockMap = new HashMap<Block, Block>();
51+
var blockEntityReplace = new HashMap<String, String>();
52+
for (int i = 0; i < teamList.size(); i++) {
53+
var baseColor = this.baseColors.get(i);
54+
var teamColor = teamList.get(i).config().blockDyeColor();
55+
blockMap.put(ColoredBlocks.wool(baseColor), ColoredBlocks.wool(teamColor));
56+
blockMap.put(ColoredBlocks.carpet(baseColor), ColoredBlocks.carpet(teamColor));
57+
blockMap.put(ColoredBlocks.terracotta(baseColor), ColoredBlocks.terracotta(teamColor));
58+
blockMap.put(ColoredBlocks.glazedTerracotta(baseColor), ColoredBlocks.glazedTerracotta(teamColor));
59+
blockMap.put(ColoredBlocks.concrete(baseColor), ColoredBlocks.concrete(teamColor));
60+
blockMap.put(ColoredBlocks.concretePowder(baseColor), ColoredBlocks.concretePowder(teamColor));
61+
blockMap.put(ColoredBlocks.glass(baseColor), ColoredBlocks.glass(teamColor));
62+
blockMap.put(ColoredBlocks.glassPane(baseColor), ColoredBlocks.glassPane(teamColor));
63+
blockMap.put(ColoredBlocks.bed(baseColor), ColoredBlocks.bed(teamColor));
64+
blockMap.put(ColoredBlocks.shulkerBox(baseColor), ColoredBlocks.shulkerBox(teamColor));
65+
blockMap.put(ColoredBlocks.candle(baseColor), ColoredBlocks.candle(teamColor));
66+
blockMap.put(ColoredBlocks.candleCake(baseColor), ColoredBlocks.candleCake(teamColor));
67+
blockEntityReplace.put(Registries.ITEM.getId(ColoredItems.dye(baseColor)).toString(), Registries.ITEM.getId(ColoredItems.dye(teamColor)).toString());
68+
blockEntityReplace.put(Registries.ITEM.getId(ColoredItems.bundle(baseColor)).toString(), Registries.ITEM.getId(ColoredItems.bundle(teamColor)).toString());
69+
blockEntityReplace.put(Registries.ITEM.getId(ColoredItems.harness(baseColor)).toString(), Registries.ITEM.getId(ColoredItems.harness(teamColor)).toString());
70+
}
71+
72+
new ReplaceBlocksTemplateProcessor(blockMap).processTemplate(template, parameters);
73+
74+
for (var entry : blockMap.entrySet()) {
75+
blockEntityReplace.put(Registries.BLOCK.getId(entry.getKey()).toString(), Registries.BLOCK.getId(entry.getValue()).toString());
76+
}
77+
new ReplaceBlockEntitiesTemplateProcessor(blockEntityReplace).processTemplate(template, parameters);
78+
}
79+
}

src/main/java/xyz/nucleoid/plasmid/api/registry/PlasmidRegistries.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import xyz.nucleoid.plasmid.api.game.GameType;
1010
import xyz.nucleoid.plasmid.api.game.common.team.provider.TeamListProvider;
1111
import xyz.nucleoid.plasmid.api.game.config.GameConfig;
12+
import xyz.nucleoid.plasmid.api.map.template.processor.MapTemplateProcessor;
1213
import xyz.nucleoid.plasmid.impl.portal.GamePortalConfig;
1314
import xyz.nucleoid.plasmid.impl.portal.menu.MenuEntryConfig;
1415

@@ -17,6 +18,7 @@ public class PlasmidRegistries {
1718
public static final Registry<MapCodec<? extends GamePortalConfig>> GAME_PORTAL_CONFIG = register(PlasmidRegistryKeys.GAME_PORTAL_CONFIG);
1819
public static final Registry<MapCodec<? extends MenuEntryConfig>> MENU_ENTRY = register(PlasmidRegistryKeys.MENU_ENTRY);
1920
public static final Registry<MapCodec<? extends TeamListProvider>> TEAM_LIST_PROVIDER_TYPE = register(PlasmidRegistryKeys.TEAM_LIST_PROVIDER_TYPE);
21+
public static final Registry<MapCodec<? extends MapTemplateProcessor>> MAP_TEMPLATE_PROCESSOR_TYPE = register(PlasmidRegistryKeys.MAP_TEMPLATE_PROCESSOR_TYPE);
2022

2123
private static <T> SimpleRegistry<T> register(RegistryKey<Registry<T>> key) {
2224
return FabricRegistryBuilder.createSimple(key).buildAndRegister();

src/main/java/xyz/nucleoid/plasmid/api/registry/PlasmidRegistryKeys.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import xyz.nucleoid.plasmid.api.game.GameType;
77
import xyz.nucleoid.plasmid.api.game.common.team.provider.TeamListProvider;
88
import xyz.nucleoid.plasmid.api.game.config.GameConfig;
9+
import xyz.nucleoid.plasmid.api.map.template.processor.MapTemplateProcessor;
910
import xyz.nucleoid.plasmid.impl.Plasmid;
1011
import xyz.nucleoid.plasmid.impl.portal.GamePortalConfig;
1112
import xyz.nucleoid.plasmid.impl.portal.menu.MenuEntryConfig;
@@ -16,6 +17,7 @@ public class PlasmidRegistryKeys {
1617
public static final RegistryKey<Registry<MapCodec<? extends MenuEntryConfig>>> MENU_ENTRY = createKey("menu_entry");
1718
public static final RegistryKey<Registry<GameConfig<?>>> GAME_CONFIG = createKey("game");
1819
public static final RegistryKey<Registry<MapCodec<? extends TeamListProvider>>> TEAM_LIST_PROVIDER_TYPE = createKey("team_list_provider_type");
20+
public static final RegistryKey<Registry<MapCodec<? extends MapTemplateProcessor>>> MAP_TEMPLATE_PROCESSOR_TYPE = createKey("map_template_processor_type");
1921

2022
private static <T> RegistryKey<Registry<T>> createKey(String key) {
2123
return RegistryKey.ofRegistry(Plasmid.id(key));
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package xyz.nucleoid.plasmid.api.util;
2+
3+
import net.minecraft.item.Item;
4+
import net.minecraft.item.Items;
5+
import net.minecraft.util.DyeColor;
6+
7+
public final class ColoredItems {
8+
public static Item dye(DyeColor color) {
9+
return switch (color) {
10+
case ORANGE -> Items.ORANGE_DYE;
11+
case MAGENTA -> Items.MAGENTA_DYE;
12+
case LIGHT_BLUE -> Items.LIGHT_BLUE_DYE;
13+
case YELLOW -> Items.YELLOW_DYE;
14+
case LIME -> Items.LIME_DYE;
15+
case PINK -> Items.PINK_DYE;
16+
case GRAY -> Items.GRAY_DYE;
17+
case LIGHT_GRAY -> Items.LIGHT_GRAY_DYE;
18+
case CYAN -> Items.CYAN_DYE;
19+
case PURPLE -> Items.PURPLE_DYE;
20+
case BLUE -> Items.BLUE_DYE;
21+
case BROWN -> Items.BROWN_DYE;
22+
case GREEN -> Items.GREEN_DYE;
23+
case RED -> Items.RED_DYE;
24+
case BLACK -> Items.BLACK_DYE;
25+
case WHITE -> Items.WHITE_DYE;
26+
};
27+
}
28+
29+
public static Item bundle(DyeColor color) {
30+
return switch (color) {
31+
case ORANGE -> Items.ORANGE_BUNDLE;
32+
case MAGENTA -> Items.MAGENTA_BUNDLE;
33+
case LIGHT_BLUE -> Items.LIGHT_BLUE_BUNDLE;
34+
case YELLOW -> Items.YELLOW_BUNDLE;
35+
case LIME -> Items.LIME_BUNDLE;
36+
case PINK -> Items.PINK_BUNDLE;
37+
case GRAY -> Items.GRAY_BUNDLE;
38+
case LIGHT_GRAY -> Items.LIGHT_GRAY_BUNDLE;
39+
case CYAN -> Items.CYAN_BUNDLE;
40+
case PURPLE -> Items.PURPLE_BUNDLE;
41+
case BLUE -> Items.BLUE_BUNDLE;
42+
case BROWN -> Items.BROWN_BUNDLE;
43+
case GREEN -> Items.GREEN_BUNDLE;
44+
case RED -> Items.RED_BUNDLE;
45+
case BLACK -> Items.BLACK_BUNDLE;
46+
case WHITE -> Items.WHITE_BUNDLE;
47+
};
48+
}
49+
50+
public static Item harness(DyeColor color) {
51+
return switch (color) {
52+
case ORANGE -> Items.ORANGE_HARNESS;
53+
case MAGENTA -> Items.MAGENTA_HARNESS;
54+
case LIGHT_BLUE -> Items.LIGHT_BLUE_HARNESS;
55+
case YELLOW -> Items.YELLOW_HARNESS;
56+
case LIME -> Items.LIME_HARNESS;
57+
case PINK -> Items.PINK_HARNESS;
58+
case GRAY -> Items.GRAY_HARNESS;
59+
case LIGHT_GRAY -> Items.LIGHT_GRAY_HARNESS;
60+
case CYAN -> Items.CYAN_HARNESS;
61+
case PURPLE -> Items.PURPLE_HARNESS;
62+
case BLUE -> Items.BLUE_HARNESS;
63+
case BROWN -> Items.BROWN_HARNESS;
64+
case GREEN -> Items.GREEN_HARNESS;
65+
case RED -> Items.RED_HARNESS;
66+
case BLACK -> Items.BLACK_HARNESS;
67+
case WHITE -> Items.WHITE_HARNESS;
68+
};
69+
}
70+
}

src/main/java/xyz/nucleoid/plasmid/impl/Plasmid.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import xyz.nucleoid.plasmid.api.game.GameTypes;
2121
import xyz.nucleoid.plasmid.api.game.common.team.provider.TeamListProviderTypes;
2222
import xyz.nucleoid.plasmid.api.game.event.GameActivityEvents;
23+
import xyz.nucleoid.plasmid.api.map.template.processor.MapTemplateProcessorTypes;
2324
import xyz.nucleoid.plasmid.api.portal.GamePortalConfigs;
2425
import xyz.nucleoid.plasmid.api.portal.menu.MenuEntryConfigs;
2526
import xyz.nucleoid.plasmid.api.registry.PlasmidRegistries;
@@ -42,6 +43,7 @@ public void onInitialize() {
4243
Reflection.initialize(MenuEntryConfigs.class);
4344
Reflection.initialize(GameTypes.class);
4445
Reflection.initialize(TeamListProviderTypes.class);
46+
Reflection.initialize(MapTemplateProcessorTypes.class);
4547

4648
this.registerCallbacks();
4749

0 commit comments

Comments
 (0)