Skip to content

Commit 4675b23

Browse files
committed
in world map exporting
1 parent 977256f commit 4675b23

File tree

1 file changed

+204
-26
lines changed

1 file changed

+204
-26
lines changed

src/main/java/net/earthcomputer/clientcommands/command/MapCommand.java

Lines changed: 204 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,29 @@
66
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
77
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
88
import net.minecraft.block.MapColor;
9+
import net.minecraft.block.entity.BlockEntity;
910
import net.minecraft.client.MinecraftClient;
1011
import net.minecraft.client.network.ClientPlayerEntity;
1112
import net.minecraft.client.texture.NativeImage;
1213
import net.minecraft.client.util.ScreenshotRecorder;
14+
import net.minecraft.entity.Entity;
15+
import net.minecraft.entity.decoration.ItemFrameEntity;
1316
import net.minecraft.item.FilledMapItem;
1417
import net.minecraft.item.ItemStack;
1518
import net.minecraft.item.map.MapState;
1619
import net.minecraft.text.ClickEvent;
1720
import net.minecraft.text.Text;
21+
import net.minecraft.util.math.BlockPos;
22+
import net.minecraft.util.math.Direction;
23+
import net.minecraft.util.math.Vec3i;
24+
import net.minecraft.world.World;
1825

1926
import java.io.File;
2027
import java.io.IOException;
28+
import java.util.*;
29+
import java.util.function.Function;
30+
import java.util.stream.Collectors;
31+
import java.util.stream.StreamSupport;
2132

2233
import static com.mojang.brigadier.arguments.IntegerArgumentType.*;
2334
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*;
@@ -37,40 +48,33 @@ public static void register(CommandDispatcher<FabricClientCommandSource> dispatc
3748
);
3849
}
3950

40-
private static MapState getMapState(ClientPlayerEntity player) throws CommandSyntaxException {
41-
ItemStack map;
42-
43-
// detect if map in hand
44-
if (player.getMainHandStack().getItem() instanceof FilledMapItem) {
45-
map = player.getMainHandStack();
46-
} else if (player.getOffHandStack().getItem() instanceof FilledMapItem) {
47-
map = player.getOffHandStack();
48-
} else {
49-
throw NO_HELD_MAP_EXCEPTION.create();
51+
private static MapInfo[][] getMaps(ClientPlayerEntity player, MinecraftClient client) throws CommandSyntaxException {
52+
MapInfo hand = fromHand(player);
53+
if (hand != null) {
54+
return new MapInfo[][]{{hand}};
5055
}
51-
52-
Integer mapId = FilledMapItem.getMapId(map);
53-
MapState mapState = FilledMapItem.getMapState(mapId, player.world);
54-
if (mapState == null) {
56+
MapInfo[][] mapInfo = fromWorld(player, client.targetedEntity);
57+
if (mapInfo == null) {
5558
throw NO_HELD_MAP_EXCEPTION.create();
5659
}
57-
return mapState;
60+
return mapInfo;
5861
}
5962

6063

6164
private static int exportMap(FabricClientCommandSource source, int upscale) throws CommandSyntaxException {
62-
MapState mapState = getMapState(source.getPlayer());
63-
try (NativeImage image = new NativeImage(NativeImage.Format.RGBA, 128 * upscale, 128 * upscale, false)) {
64-
for (int i = 0; i < 128 * upscale; i+= upscale) {
65-
for (int j = 0; j < 128 * upscale; j+= upscale) {
66-
int x = i / upscale;
67-
int y = j / upscale;
68-
int color = MapColor.getRenderColor(mapState.colors[x + y * 128]);
69-
for (int k = 0; k < upscale; k++) {
70-
for (int l = 0; l < upscale; l++) {
71-
image.setColor(i + k, j + l, color);
72-
}
65+
MapInfo[][] mapInfo = getMaps(source.getPlayer(), source.getClient());
66+
// calculate width and height
67+
int width = mapInfo[0].length * 128 * upscale;
68+
int height = mapInfo.length * 128 * upscale;
69+
70+
try (NativeImage image = new NativeImage(NativeImage.Format.RGBA, width, height, true)) {
71+
for (int i = 0; i < mapInfo.length; ++i) {
72+
for (int j = 0; j < mapInfo[i].length; ++j) {
73+
MapInfo info = mapInfo[i][j];
74+
if (info == null) {
75+
continue;
7376
}
77+
drawMapOffset(image, info, j * 128 * upscale, i * 128 * upscale, upscale);
7478
}
7579
}
7680
File screenshotDir = new File(source.getClient().runDirectory, "screenshots");
@@ -88,4 +92,178 @@ private static int exportMap(FabricClientCommandSource source, int upscale) thro
8892
return Command.SINGLE_SUCCESS;
8993
}
9094

95+
private static void drawMapOffset(NativeImage image, MapInfo map, int xOff, int yOff, int upscale) {
96+
for (int i = 0; i < 128; ++i) {
97+
for (int j = 0; j < 128; ++j) {
98+
int color = MapColor.getRenderColor(map.getColor(i, j));
99+
for (int k = 0; k < upscale; k++) {
100+
for (int l = 0; l < upscale; l++) {
101+
image.setColor(i * upscale + k + xOff, j * upscale + l + yOff, color);
102+
}
103+
}
104+
}
105+
}
106+
}
107+
108+
private static MapInfo fromHand(ClientPlayerEntity player) throws CommandSyntaxException {
109+
ItemStack map;
110+
111+
// detect if map in hand
112+
if (player.getMainHandStack().getItem() instanceof FilledMapItem) {
113+
map = player.getMainHandStack();
114+
} else if (player.getOffHandStack().getItem() instanceof FilledMapItem) {
115+
map = player.getOffHandStack();
116+
} else {
117+
return null;
118+
}
119+
120+
return new MapInfo(fromItemStack(player.world, map), 0);
121+
}
122+
123+
private static MapInfo[][] fromWorld(ClientPlayerEntity player, Entity target) {
124+
if (target instanceof ItemFrameEntity frame) {
125+
ItemStack map = frame.getHeldItemStack();
126+
if (map == null || !(map.getItem() instanceof FilledMapItem)) {
127+
return null;
128+
}
129+
Direction facing = frame.getHorizontalFacing();
130+
// find in world
131+
BlockPos pos = frame.getBlockPos();
132+
FramePos current = new FramePos(pos, facing);
133+
// get all loaded item frames
134+
Map<FramePos, ItemFrameEntity> entities = StreamSupport.stream(player.clientWorld.getEntities().spliterator(), true).filter(e -> e instanceof ItemFrameEntity && ((ItemFrameEntity) e).getMapId().isPresent()).map(e -> (ItemFrameEntity) e).collect(Collectors.toMap(e -> new FramePos(e.getBlockPos(), e.getHorizontalFacing()), Function.identity()));
135+
Set<ItemFrameEntity> connected = new HashSet<>();
136+
getConnectedItemFrames(connected, entities, current);
137+
// put them in proper traversal order
138+
Direction yAxis = switch (facing) {
139+
case NORTH, SOUTH, EAST, WEST -> Direction.DOWN;
140+
case UP -> player.getHorizontalFacing().getOpposite();
141+
case DOWN -> player.getHorizontalFacing();
142+
};
143+
Direction xAxis = switch (facing) {
144+
case NORTH -> Direction.WEST;
145+
case EAST -> Direction.NORTH;
146+
case SOUTH -> Direction.EAST;
147+
case WEST -> Direction.SOUTH;
148+
case UP -> Direction.fromHorizontal((yAxis.getHorizontal() + 3) % 4);
149+
case DOWN -> Direction.fromHorizontal((yAxis.getHorizontal() + 1) % 4);
150+
};
151+
Map<Vec2i, ItemFrameEntity> flattened = new HashMap<>();
152+
Iterator<ItemFrameEntity> iterator = connected.iterator();
153+
ItemFrameEntity origin = iterator.next();
154+
flattened.put(new Vec2i(0, 0), origin);
155+
BlockPos zero = origin.getBlockPos();
156+
while (iterator.hasNext()) {
157+
ItemFrameEntity next = iterator.next();
158+
// calculate offset in 2d from 0 0
159+
BlockPos nextPos = next.getBlockPos();
160+
Vec3i vec = nextPos.subtract(zero);
161+
int y = vec.getComponentAlongAxis(yAxis.getAxis()) * yAxis.getDirection().offset();
162+
int x = vec.getComponentAlongAxis(xAxis.getAxis()) * xAxis.getDirection().offset();
163+
// this should never be possible
164+
if (flattened.put(new Vec2i(x, y), next) != null) throw new IllegalStateException("Duplicate item frame at " + nextPos);
165+
}
166+
System.out.println(flattened);
167+
// find min and max
168+
Iterator<Vec2i> vec2iIterator = flattened.keySet().iterator();
169+
Vec2i first = vec2iIterator.next();
170+
int minX = first.x();
171+
int maxX = first.x();
172+
int minY = first.z();
173+
int maxY = first.z();
174+
while (vec2iIterator.hasNext()) {
175+
Vec2i next = vec2iIterator.next();
176+
minX = Math.min(minX, next.x());
177+
maxX = Math.max(maxX, next.x());
178+
minY = Math.min(minY, next.z());
179+
maxY = Math.max(maxY, next.z());
180+
}
181+
// create array
182+
MapInfo[][] mapInfo = new MapInfo[maxY - minY + 1][maxX - minX + 1];
183+
for (Map.Entry<Vec2i, ItemFrameEntity> entry : flattened.entrySet()) {
184+
Vec2i pos2 = entry.getKey();
185+
ItemFrameEntity entity = entry.getValue();
186+
int rotationOffset = 0;
187+
if (yAxis != Direction.DOWN) {
188+
rotationOffset = switch (yAxis) {
189+
case WEST -> 3;
190+
case EAST -> 1;
191+
case SOUTH -> facing == Direction.UP ? 0 : 2;
192+
default -> facing == Direction.UP ? 2 : 0;
193+
};
194+
}
195+
mapInfo[pos2.z() - minY][pos2.x() - minX] = new MapInfo(fromItemFrame(entry.getValue()), entity.getRotation() + rotationOffset);
196+
}
197+
return mapInfo;
198+
}
199+
return null;
200+
}
201+
202+
private static void getConnectedItemFrames(Set<ItemFrameEntity> already, Map<FramePos, ItemFrameEntity> frames, FramePos current) {
203+
if (frames.containsKey(current)) {
204+
ItemFrameEntity frame = frames.get(current);
205+
if (!already.contains(frame)) {
206+
already.add(frame);
207+
BlockPos[] adjacent = switch (current.facing().getAxis()) {
208+
case X -> new BlockPos[]{current.pos().north(), current.pos().south(), current.pos().up(), current.pos().down()};
209+
case Y -> new BlockPos[]{current.pos().north(), current.pos().south(), current.pos().east(), current.pos().west()};
210+
case Z -> new BlockPos[]{current.pos().east(), current.pos().west(), current.pos().up(), current.pos().down()};
211+
};
212+
for (BlockPos pos : adjacent) {
213+
getConnectedItemFrames(already, frames, new FramePos(pos, current.facing()));
214+
}
215+
}
216+
}
217+
}
218+
219+
private static MapState fromItemStack(World world, ItemStack map) throws CommandSyntaxException {
220+
Integer mapId = FilledMapItem.getMapId(map);
221+
MapState mapState = FilledMapItem.getMapState(mapId, world);
222+
if (mapState == null) {
223+
throw NO_HELD_MAP_EXCEPTION.create();
224+
}
225+
226+
return mapState;
227+
}
228+
229+
private static MapState fromItemFrame(ItemFrameEntity entity) {
230+
Integer mapId = entity.getMapId().orElseThrow();
231+
return FilledMapItem.getMapState(mapId, entity.world);
232+
}
233+
234+
private record MapInfo(MapState state, int rotation) {
235+
236+
public int getColor(int x, int y) {
237+
// rotate x/y
238+
switch (rotation % 4) {
239+
case 0 -> {
240+
return state.colors[x + y * 128];
241+
}
242+
case 1 -> {
243+
// 90 clockwise
244+
int newX = y;
245+
int newY = 127 - x;
246+
return state.colors[newX + newY * 128];
247+
}
248+
case 2 -> {
249+
// 180 clockwise
250+
int newX = 127 - x;
251+
int newY = 127 - y;
252+
return state.colors[newX + newY * 128];
253+
}
254+
case 3 -> {
255+
// 270 clockwise
256+
int newX = 127 - y;
257+
int newY = x;
258+
return state.colors[newX + newY * 128];
259+
}
260+
// this should never be possible
261+
default -> throw new IllegalStateException("Unexpected value: " + rotation);
262+
}
263+
}
264+
}
265+
266+
private record FramePos(BlockPos pos, Direction facing) {
267+
}
268+
91269
}

0 commit comments

Comments
 (0)