Sign in to follow this  
Friya

[Released] GM Goto (server)

Recommended Posts

#
# A little mod to make my life easier; provides the #goto command for GM's.
# I was SO fed up with having to use the wand.
# That's it.
#
# Someone else probably already made a mod for this and I just did not know about it.
#
# Usage:  Type #goto in any tab.
#
# Syntax: #goto <x y> 
#         or #goto <deed name>
#         or #goto <player name>
#
#         If the deed is multiple words, use quotes. E.g. #goto "Friya's Fantastic Fellowship"
#         If there is a deed called Friya and also a player with the same name, distinguish the two by doing #goto deed Friya or #goto player Friya
#         If going to coordinate, make sure it is within the map boundaries.
#         You cannot go to invisible GMs.
#
#                - Friya, 2018
#

 

Download

 

And here's the source code for anyone wanting to do ... more.

 

package com.friya.wurmonline.server.admin;

import java.util.ArrayList;
import java.util.StringTokenizer;

import org.gotti.wurmunlimited.modloader.interfaces.PlayerMessageListener;
import org.gotti.wurmunlimited.modloader.interfaces.WurmServerMod;

import com.wurmonline.server.Players;
import com.wurmonline.server.creatures.Communicator;
import com.wurmonline.server.players.Player;
import com.wurmonline.server.villages.NoSuchVillageException;
import com.wurmonline.server.villages.Village;
import com.wurmonline.server.villages.Villages;
import com.wurmonline.server.zones.Zones;

public class Mod implements WurmServerMod, PlayerMessageListener
{
	@Override
	public boolean onPlayerMessage(Communicator com, String msg)
	{
		if(com.getPlayer().getPower() > 0) {
			// Verbose because I -may- want to add more GM commands.
			if(msg.startsWith("#goto")) {
				return cmdGoto(com, msg);
			}
		}
		
		return false;
	}

	private boolean cmdGoto(Communicator com, String cmd)
	{
		String[] tokens = translateCommandline(cmd);
		Location loc = null;
		
		if(tokens.length == 1) {
			tell(com, "Syntax:");
			tell(com, "        #goto <x y> or #goto <deed name> or #goto <player name>");
			tell(com, "Examples:");
			tell(com, "        #goto 500 500");
			tell(com, "        #goto \"Friya's Home\"");
			tell(com, "        #goto friya");
			tell(com, "Notes:");
			tell(com, "        If the deed name is multiple words, use quotes. E.g. #goto \"Friya's Fantastic Fellowship\"");
			tell(com, "        If there is a deed called Friya and also a player with the sane name, separate the two by doing #goto deed Friya or #goto player Friya");
			tell(com, "        If going to coordinate, make sure it is within the map boundaries.");
			tell(com, "        You cannot go to invisible GMs.");
			return true;
		}
		
		if(tokens.length == 3) {
			if(isNumericUgly(tokens[1]) && isNumericUgly(tokens[2])) {
				// #goto x y
				loc = getCoordinateLocation(tokens[1], tokens[2]);
				
			} else if(tokens[1].equals("deed")) {
				// #goto deed
				loc = getDeedLocationByName(tokens[2]);

			} else if(tokens[1].equals("player")) {
				// #goto player
				loc = getPlayerLocationByName(tokens[2]);
			}

		} else if(tokens.length == 2) {
			// #goto <player name>
			loc = getPlayerLocationByName(tokens[1]);

			if(loc == null) {
				// #goto <deed name>
				loc = getDeedLocationByName(tokens[1]);
			}
		}

		if(loc == null || gotoTarget(com, loc) == false) {
			tell(com, "No idea how to go there... Typo? Coordinate is outside map? Target is invisible? Not a player? Already teleporting?");
		} else {
			tell(com, "The fabric of space opens and you appear at " + loc.getName() + " ... or so you hope.");
		}

		return true;
	}

	private boolean gotoTarget(Communicator c, Location loc)
	{
		Player p = c.getPlayer();

		p.setTeleportPoints((short)loc.getX(), (short)loc.getY(), loc.getLayer(), 0);

		if (p.startTeleporting()) {
			c.sendTeleport(false);
			p.teleport();
			return true;
		}   

		return false;
	}
	
	private Location getCoordinateLocation(String sx, String sy)
	{
		int x = Integer.parseInt(sx);
		int y = Integer.parseInt(sy);

		if(x < 1 || x > Zones.worldTileSizeX || y < 1 || y > Zones.worldTileSizeY) {
			return null;
		}
		
		return new Location(x + "x" + y, x, y, 0);
	}
	
	private Location getPlayerLocationByName(String name)
	{
		Player p = Players.getInstance().getPlayerOrNull(name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase());

		// if target is an invisible GM, disallow going to them (caller will give a generic error)
		if(p == null || (p.getPower() > 0 && p.isVisible() == false)) {
			return null;
		}
		
		return new Location(p.getName(), p.getTileX(), p.getTileY(), p.getLayer());
	}
	
	private Location getDeedLocationByName(String name)
	{
		Village d = null;

		try {
			d = Villages.getVillage(name);
		} catch (NoSuchVillageException e) {
		}
		
		if(d == null) {
			return null;
		}
		
		return new Location(d.getName(), d.getTokenX(), d.getTokenY(), 0);
	}
	
	/**
	 * Crack a command line.
	 * 
	 * Shamelessly lifted from another mod of mine which shamelessly lifted it from 
	 * Apache Commons. See https://commons.apache.org/proper/commons-exec/apidocs/src-html/org/apache/commons/exec/CommandLine.html
	 * 
	 * @param toProcess the command line to process.
	 * @return the command line broken into strings.
	 * 
	 * An empty or null toProcess parameter results in a zero sized array.
	 */
	private String[] translateCommandline(String toProcess)
	{
		if (toProcess == null || toProcess.length() == 0) {
			return new String[0];
		}
		
		final int normal = 0;
		final int inQuote = 1;
		final int inDoubleQuote = 2;
		int state = normal;
		final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
		final ArrayList<String> result = new ArrayList<String>();
		final StringBuilder current = new StringBuilder();
		boolean lastTokenHasBeenQuoted = false;
		
		while (tok.hasMoreTokens()) {
			String nextTok = tok.nextToken();

			switch (state) {
				case inQuote:
					if ("\'".equals(nextTok)) {
						lastTokenHasBeenQuoted = true;
						state = normal;
					} else {
						current.append(nextTok);
					}
					break;

				case inDoubleQuote:
					if ("\"".equals(nextTok)) {
						lastTokenHasBeenQuoted = true;
						state = normal;
					} else {
						current.append(nextTok);
					}
			        break;
			        
				default:
					if ("\'".equals(nextTok)) {
						state = inQuote;
					} else if ("\"".equals(nextTok)) {
						state = inDoubleQuote;
					} else if (" ".equals(nextTok)) {
						if (lastTokenHasBeenQuoted || current.length() != 0) {
							result.add(current.toString());
							current.setLength(0);
						}
					} else {
						current.append(nextTok);
					}
					lastTokenHasBeenQuoted = false;
					break;
			}
		}
		
		if (lastTokenHasBeenQuoted || current.length() != 0) {
		    result.add(current.toString());
		}
		
		if (state == inQuote || state == inDoubleQuote) {
		    throw new RuntimeException("unbalanced quotes in " + toProcess);
		}
		
		return result.toArray(new String[result.size()]);
	}


	/**
	 * Despite this using an exception for functionality, I am okay with it. It's executed rarely enough and definitely rare enough to trigger the exception.
	 * 
	 * @param str
	 * @return
	 */
	private boolean isNumericUgly(String str)  
	{  
		try {
			@SuppressWarnings("unused")
			double d = Double.parseDouble(str);  
		}  
		catch(NumberFormatException nfe) {  
			return false;  
		}
	
		return true;  
	}

	private void tell(Communicator c, String msg)
	{
		c.sendNormalServerMessage(msg);
	}
}

/* ------------------------------------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------------------------------------- */

package com.friya.wurmonline.server.admin;

class Location
{
	private String _name;
	private int _x;
	private int _y;
	private int _layer = 0;

	Location(String name, int x, int y, int layer)
	{
		_name = name;
		_x = x;
		_y = y;
		_layer = layer;
	}

	String getName() { return _name; }
	int getX() { return _x; }
	int getY() { return _y; }
	public int getLayer() { return _layer; }
}

 

Edited by Friya
  • Like 3

Share this post


Link to post
Share on other sites
On 5/16/2018 at 7:01 AM, Friya said:

# Syntax:

#goto <x y> # or

#goto <deed name> # or

#goto <player name>

Comma or space needed between the x y coordinates?  For example, will this work?

#goto 1200,1200

 

Also, will this work for all levels of GM, or just those normally using an ebony wand?  <---  If that's a dumb question, then thanks for your patience.  I'm still figuring this stuff out.  ?

 

 

Edited by Batta

Share this post


Link to post
Share on other sites

Turns out that the #goto coordinates works only if there is a space but no comma, like this:  #goto 1200 900

For the next guy, also note that deed names that are longer than 1 word must have quotation marks around them:  #goto "Spawn Island"

 

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this