Skip to content

Commit 84683dd

Browse files
committed
Auto Portal Logging
1 parent 6a62ff4 commit 84683dd

File tree

4 files changed

+569
-11
lines changed

4 files changed

+569
-11
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ I did not, nor could I copy their code directly as most are Meteor based mods. S
158158
- Stored per world/server under `wurst/waypoints/<worldId>.json`.
159159
- Xaero's Minimap integration, allows exporting and importing of waypoint data (disconnect & reconnect to update).
160160
- Adjustable Tri-state Beacon Mode on waypoints (On/Off/ESP) that matches the waypoint's color.
161+
- Toggleable automatic portal logging, logs numerically each portal you enter and matches it in the opposite dimension with the same number.
161162

162163
![WayPoints](https://i.imgur.com/dxFc17N.png) ![Waypoint Manager](https://i.imgur.com/K2xGVqc.png) ![Waypoint Editor](https://i.imgur.com/23i14s1.png)
163164

src/main/java/net/wurstclient/command/CmdList.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ public final class CmdList
7676
public final ViewNbtCmd viewNbtCmd = new ViewNbtCmd();
7777
public final SurfaceXrayCmd surfaceXrayCmd = new SurfaceXrayCmd();
7878
public final XrayCmd xrayCmd = new XrayCmd();
79+
public final net.wurstclient.commands.WaypointCmd waypointCmd =
80+
new net.wurstclient.commands.WaypointCmd();
7981
public final net.wurstclient.commands.WaypointsCmd waypointsCmd =
8082
new net.wurstclient.commands.WaypointsCmd();
8183

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
/*
2+
* Copyright (c) 2014-2025 Wurst-Imperium and contributors.
3+
*
4+
* This source code is subject to the terms of the GNU General Public
5+
* License, version 3. If a copy of the GPL was not distributed with this
6+
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
7+
*/
8+
package net.wurstclient.commands;
9+
10+
import java.util.Arrays;
11+
import java.util.LinkedHashMap;
12+
import java.util.Locale;
13+
import java.util.Map;
14+
import java.util.Set;
15+
import java.util.UUID;
16+
import net.minecraft.core.BlockPos;
17+
import net.wurstclient.command.CmdError;
18+
import net.wurstclient.command.CmdException;
19+
import net.wurstclient.command.CmdSyntaxError;
20+
import net.wurstclient.command.Command;
21+
import net.wurstclient.util.ChatUtils;
22+
import net.wurstclient.waypoints.Waypoint;
23+
import net.wurstclient.waypoints.WaypointDimension;
24+
25+
public final class WaypointCmd extends Command
26+
{
27+
private static final Set<String> VALID_ICONS =
28+
Set.of("square", "circle", "triangle", "star", "diamond", "skull",
29+
"heart", "check", "x", "arrow_down", "sun", "snowflake");
30+
31+
public WaypointCmd()
32+
{
33+
super("waypoint", "Create waypoints via chat.",
34+
".waypoint add <name> [x=<int>] [y=<int>] [z=<int>] [dim=<overworld|nether|end>]"
35+
+ " [color=<#RRGGBB|#AARRGGBB>] [icon=<"
36+
+ String.join("|", VALID_ICONS)
37+
+ ">] [visible=<true|false>] [lines=<true|false>]"
38+
+ " [opposite=<true|false>] [beacon=<off|solid|esp>]"
39+
+ " [action=<disabled|hide|delete>] [actiondist=<int>]"
40+
+ " [maxvisible=<int>] [scale=<decimal>]");
41+
}
42+
43+
@Override
44+
public void call(String[] args) throws CmdException
45+
{
46+
if(args.length == 0)
47+
throw new CmdSyntaxError("Missing subcommand.");
48+
49+
String sub = args[0].toLowerCase(Locale.ROOT);
50+
switch(sub)
51+
{
52+
case "add":
53+
handleAdd(Arrays.copyOfRange(args, 1, args.length));
54+
break;
55+
56+
default:
57+
throw new CmdSyntaxError("Unknown subcommand: " + sub);
58+
}
59+
}
60+
61+
private void handleAdd(String[] args) throws CmdException
62+
{
63+
if(args.length == 0)
64+
throw new CmdSyntaxError("Missing waypoint name.");
65+
66+
Map<String, String> options = new LinkedHashMap<>();
67+
String name = null;
68+
for(String arg : args)
69+
{
70+
int eq = arg.indexOf('=');
71+
if(eq < 0)
72+
{
73+
if(name != null)
74+
throw new CmdError("Unexpected argument \"" + arg + "\".");
75+
name = arg;
76+
continue;
77+
}
78+
79+
String key = arg.substring(0, eq).toLowerCase(Locale.ROOT);
80+
String value = arg.substring(eq + 1);
81+
options.put(key, value);
82+
}
83+
84+
if(options.containsKey("name"))
85+
name = options.remove("name");
86+
87+
if(name == null || name.isEmpty())
88+
throw new CmdError("Waypoint name is required.");
89+
name = name.replace('_', ' ');
90+
91+
BlockPos playerPos = MC.player == null ? null
92+
: BlockPos.containing(MC.player.position());
93+
Integer px = playerPos == null ? null : playerPos.getX();
94+
Integer py = playerPos == null ? null : playerPos.getY();
95+
Integer pz = playerPos == null ? null : playerPos.getZ();
96+
97+
int x = parseInt(options.remove("x"), "x", px);
98+
int y = parseInt(options.remove("y"), "y", py);
99+
int z = parseInt(options.remove("z"), "z", pz);
100+
101+
WaypointDimension dim = parseDimension(options.remove("dim"));
102+
int color = parseColor(options.remove("color"));
103+
String icon = parseIcon(options.remove("icon"));
104+
boolean visible =
105+
parseBoolean(options.remove("visible"), true, "visible");
106+
boolean lines = parseBoolean(options.remove("lines"), false, "lines");
107+
boolean opposite =
108+
parseBoolean(options.remove("opposite"), false, "opposite");
109+
Waypoint.BeaconMode beacon = parseBeaconMode(options.remove("beacon"));
110+
Waypoint.ActionWhenNear action = parseAction(options.remove("action"));
111+
int actionDistance =
112+
parsePositiveInt(options.remove("actiondist"), "actiondist", 8, 1);
113+
int maxVisible = parsePositiveInt(options.remove("maxvisible"),
114+
"maxvisible", 5000, 0);
115+
double scale = parseScale(options.remove("scale"));
116+
117+
if(!options.isEmpty())
118+
{
119+
String unknown = options.keySet().iterator().next();
120+
throw new CmdError("Unknown option \"" + unknown + "\".");
121+
}
122+
123+
Waypoint waypoint =
124+
new Waypoint(UUID.randomUUID(), System.currentTimeMillis());
125+
waypoint.setName(name);
126+
waypoint.setPos(new BlockPos(x, y, z));
127+
waypoint.setDimension(dim);
128+
waypoint.setColor(color);
129+
waypoint.setIcon(icon);
130+
waypoint.setVisible(visible);
131+
waypoint.setLines(lines);
132+
waypoint.setOpposite(opposite);
133+
waypoint.setBeaconMode(beacon);
134+
waypoint.setActionWhenNear(action);
135+
waypoint.setActionWhenNearDistance(actionDistance);
136+
waypoint.setMaxVisible(maxVisible);
137+
waypoint.setScale(scale);
138+
139+
WURST.getHax().waypointsHack.addWaypointFromCommand(waypoint);
140+
ChatUtils
141+
.message(String.format("Added waypoint \"%s\" at %d, %d, %d in %s.",
142+
name, x, y, z, dim.name()));
143+
}
144+
145+
private int parseInt(String raw, String key, Integer fallback)
146+
throws CmdException
147+
{
148+
if(raw != null)
149+
return parseMandatoryInt(raw, key);
150+
if(fallback != null)
151+
return fallback;
152+
throw new CmdError("Missing value for " + key + ".");
153+
}
154+
155+
private int parseMandatoryInt(String raw, String key) throws CmdException
156+
{
157+
try
158+
{
159+
return Integer.parseInt(raw);
160+
}catch(NumberFormatException e)
161+
{
162+
throw new CmdError("Invalid integer for " + key + ": " + raw);
163+
}
164+
}
165+
166+
private int parsePositiveInt(String raw, String key, int fallback, int min)
167+
throws CmdException
168+
{
169+
if(raw == null)
170+
return Math.max(min, fallback);
171+
int value = parseMandatoryInt(raw, key);
172+
if(value < min)
173+
throw new CmdError(key + " must be >= " + min + ".");
174+
return value;
175+
}
176+
177+
private double parseScale(String raw) throws CmdException
178+
{
179+
if(raw == null)
180+
return 1.5;
181+
try
182+
{
183+
double value = Double.parseDouble(raw);
184+
return Math.max(0.1, Math.min(10.0, value));
185+
}catch(NumberFormatException e)
186+
{
187+
throw new CmdError("Invalid scale: " + raw);
188+
}
189+
}
190+
191+
private boolean parseBoolean(String raw, boolean fallback, String key)
192+
throws CmdException
193+
{
194+
if(raw == null)
195+
return fallback;
196+
String value = raw.toLowerCase(Locale.ROOT);
197+
switch(value)
198+
{
199+
case "true":
200+
case "1":
201+
case "yes":
202+
case "on":
203+
return true;
204+
205+
case "false":
206+
case "0":
207+
case "no":
208+
case "off":
209+
return false;
210+
211+
default:
212+
throw new CmdError("Invalid boolean for " + key + ": " + raw);
213+
}
214+
}
215+
216+
private Waypoint.BeaconMode parseBeaconMode(String raw) throws CmdException
217+
{
218+
if(raw == null)
219+
return Waypoint.BeaconMode.OFF;
220+
String value = raw.toLowerCase(Locale.ROOT);
221+
return switch(value)
222+
{
223+
case "off", "none", "false", "disabled" -> Waypoint.BeaconMode.OFF;
224+
case "solid", "on" -> Waypoint.BeaconMode.SOLID;
225+
case "esp", "beam", "true" -> Waypoint.BeaconMode.ESP;
226+
default -> throw new CmdError("Unknown beacon mode: " + raw);
227+
};
228+
}
229+
230+
private Waypoint.ActionWhenNear parseAction(String raw) throws CmdException
231+
{
232+
if(raw == null || raw.isEmpty())
233+
return Waypoint.ActionWhenNear.DISABLED;
234+
String v = raw.toUpperCase(Locale.ROOT);
235+
try
236+
{
237+
return Waypoint.ActionWhenNear.valueOf(v);
238+
}catch(IllegalArgumentException e)
239+
{
240+
throw new CmdError("Unknown action: " + raw);
241+
}
242+
}
243+
244+
private String parseIcon(String raw) throws CmdException
245+
{
246+
if(raw == null || raw.isEmpty())
247+
return "star";
248+
String value = raw.toLowerCase(Locale.ROOT);
249+
if(!VALID_ICONS.contains(value))
250+
throw new CmdError("Unknown icon: " + raw);
251+
return value;
252+
}
253+
254+
private int parseColor(String raw) throws CmdException
255+
{
256+
if(raw == null || raw.isEmpty())
257+
return 0xFFFFFFFF;
258+
String value = raw.trim();
259+
if(value.startsWith("#"))
260+
value = value.substring(1);
261+
if(value.startsWith("0x") || value.startsWith("0X"))
262+
value = value.substring(2);
263+
if(value.length() != 6 && value.length() != 8)
264+
throw new CmdError("Color must be RRGGBB or AARRGGBB.");
265+
try
266+
{
267+
int color = (int)Long.parseLong(value, 16);
268+
if(value.length() == 6)
269+
color |= 0xFF000000;
270+
return color;
271+
}catch(NumberFormatException e)
272+
{
273+
throw new CmdError("Invalid color: " + raw);
274+
}
275+
}
276+
277+
private WaypointDimension parseDimension(String raw) throws CmdException
278+
{
279+
if(raw == null || raw.isEmpty())
280+
return currentDimension();
281+
String v = raw.toLowerCase(Locale.ROOT);
282+
return switch(v)
283+
{
284+
case "overworld", "over", "ow" -> WaypointDimension.OVERWORLD;
285+
case "nether", "hell" -> WaypointDimension.NETHER;
286+
case "end", "the_end" -> WaypointDimension.END;
287+
default -> throw new CmdError("Unknown dimension: " + raw);
288+
};
289+
}
290+
291+
private WaypointDimension currentDimension()
292+
{
293+
if(MC.level == null)
294+
return WaypointDimension.OVERWORLD;
295+
String key = MC.level.dimension().location().getPath();
296+
return switch(key)
297+
{
298+
case "the_nether" -> WaypointDimension.NETHER;
299+
case "the_end" -> WaypointDimension.END;
300+
default -> WaypointDimension.OVERWORLD;
301+
};
302+
}
303+
}

0 commit comments

Comments
 (0)