def zonemodify(message, user, zone, key, value): if name not in zones.get_zones(): tell(user, "Zone does not exist.") return zone = zones.get_zones()[zone] zone[key] = eval(value) tell(user, "Success")
def from_cube_pt2(user, value, name, point1): try: point2 = get_point(user, value) except ValueError: return from_cube_pt1(user, ",".join([str(x) for x in point1]), name) tell(user, "Set second point at (%.2f,%.2f,%.2f)" % tuple(point2)) return make_zone(user, name, ('cube', user.dimension, point1, point2))
def aclset(message, user, zone, name, perms): perms = filter(None, perms.split()) if perms is not None else [] for perm in perms: if perm not in ALL_PERMS: tell(user, "%s not an ACL." % perm) return zone['acls'][name] = perms tell(user, "Success")
def from_cyl_height(user, value, name, point, radius): try: height = float(value) if height <= 0: raise ValueError() except ValueError: tell(user, "%s not a valid positive number." % value) return from_cyl_radius(user, str(radius), name, point) tell(user, "Set height as %.2f" % height) return make_zone(user, name, ('cylinder', user.dimension, point, radius, height))
def aclhelp(message, user): tell(user, "ACLs are my equivilent of plot protection.\n" "A zone (see /zone help) has permissions for different " "users to do different things. The creator of a zone can change these. " "The permissions available are:\n" "ENTRY - Without this permission, you can't enter the zone.\n" "INTERACT - Without this permission, you can't flip levers, open doors, etc. " "Note: even with this permission, you need to not be holding a block in your hands for it to work.\n" "MODIFY - Without this permission, you can't place or dig blocks.\n" "ADMIN - This permission allows you to change the permissions of this zone.\n" "Type /acls commands for help on how to modify permissions." "Type /acls op for help on op-only commands.")
def zonehere(message, user): flag = False for zone in user.zones: if 'acls' in zone: perms = zone['acls'].get(user.username, zone['acls']['EVERYONE']) perms = ": " + " ".join(perms) else: perms = "" tell(user, zone['name'] + perms) flag = True if not flag: tell(user, "In no zones.")
def ophelp(message, user): tell(user, "Note that unlike /zone commands, zone names are taken verbatim (no quotes).\n" "/zone op help - Obvious.\n" "/zone op wool <zone> - 10x10x10 cube below player highlighted as follows:\n" "__ green: In given zone.\n" "__ yellow: In zone besides one given.\n" "__ red: In given zone AND at least one other.\n" "__ You will need to log out/in to reverse changes.\n" "/wool - Shortcut to re-run last wool command.\n" "/zone op wool-all <zone> - Does wool for an entire zone.\n" "/zone op confirm <zone> - Confirm zone.\n" '/zone op modify "<zone>" <key> <value> - Advanced use. Quotes required.')
def zonewool(message, user, name): user.morewool = name try: zone = zones.get_zones()[name] except KeyError: tell(user, "Bad zone name") return base = [int(axis) for axis in user.position] for x in range(base[0]-5, base[0]+5): for y in range(base[1]-10, base[1]): for z in range(base[2]-5, base[2]+5): wool_point(zone, user, (x,y,z)) tell(user, "Complete.")
def helplist(message, user): tell(user, "The /zone commands let you manage your zones.\n" "In all commands below, <zone> should be replaced with\n" "the name of a zone you control, eg. /zone view my_zone\n" "If the name has a space in it, put it in double quotes.\n" "/zone current - See a list of zones you control that you are currently in.\n" "/zone view - See a list of zones you control.\n" "/zone view <zone> - Get info on that zone.\n" "/zone here - Get list of zones you are currently in, and your permissions.\n" "/zone new - Create new zone using menu system.\n" "/zone new <zone> <args> - Advanced only, see help page.\n" "/zone destroy <zone> - Delete a zone permanently.\n" "/zone help <command> - Get help on a command, eg. /zone help new\n")
def zonewoolall(message, user, name): yield EDGE = 4 # extra blocks beyond edges user.morewool = name try: zone = zones.get_zones()[name] except KeyError: tell(user, "Bad zone name") return if zone['bounds_info'][0] == 'cube': box = zone['bounds_info'][2:] box = zip(*box) box = [sorted(axis) for axis in box] elif zone['bounds_info'][0] == 'cylinder': (x, y, z), r, h = zone['bounds_info'][2:] box = ((x-r, x+r), (y, y+h), (z-r, z+r)) else: tell(user, "Unsupported zone type: %s" % zone['bounds_info'][0]) return box = [(int(a)-EDGE, int(b)+1+EDGE) for a,b in box] for y in range(*box[1]): for x in range(*box[0]): yield for z in range(*box[2]): wool_point(zone, user, (x,y,z)) complete = float(y-box[1][0])/(box[1][1]-box[1][0]) if complete < 1: tell(user, 'Wooling %s...%.2f%% complete' % (name, 100 * complete)) tell(user, 'Complete.')
def get_point(user, value): if not value: x,y,z = user.position return (x, y, z) try: point = [float(x.strip()) for x in value.split(',')] except ValueError: point = [] if len(point) != 3: tell(user, "Badly formatted response. For example,\n" "the point at x=100, y=64, z=-100 would be typed as\n" ":100,64,-100") raise ValueError return point
def from_name(user, name): if name in get_zones(): tell(user, "That name is already in use.") return ROOT_MENU return ( "The new zone will be called %s.\n" "Please pick a basic shape for the new zone.\n" % name, [ ("Cube", lambda u,v: from_cube(u, v, name)), ("Cylinder", lambda u,v: from_cyl(u, v, name)) ], exit_menu )
def from_cyl_radius(user, value, name, point): try: radius = float(value) if radius <= 0: raise ValueError() except ValueError: tell(user, "%s not a valid positive number." % value) return from_cyl_pt(user, ",".join([str(x) for x in point]), name) return ( "Set radius as %.2f\n" "Please give the zone height (how far up from the base point)." % radius, ('prompt', lambda u,v: from_cyl_height(u, v, name, point, radius)), exit_menu )
def wrapped_fn(message, user, zone, name, *args): zone_original = zone zone = zone.strip('"') if zone not in get_zones(): tell(user, "Zone does not exist.") print zone return zone = get_zones()[zone] if 'acls' not in zone: tell(user, "Sorry, that zone does not appear to support ACLs.\n" "You may need to contact an op to resolve this.") return if name != 'EVERYONE': name = name.lower() if name not in all_users() and name not in zone['acls']: tell(user, "Warning: %s is not a player known to this server.\n" "If you have made a mistake, type:\n/acls %s clear %s" % (zone_original, name)) if ADMIN not in zone['acls'].get(user.username, zone['acls']['EVERYONE']) and user.username not in ops(): tell(user, "You do not have permission to modify ACLs for this zone. " "If you have accidentially locked yourself out, please contact an op for assistance.") return return fn(message, user, zone, name, *args)
def aclcmdhelp(message, user): tell(user, "ACL commands:\n" "/acls <zone> set <user> <permissions>\n" "__ Set the user's permissions in that zone to exactly\n" "__ the permissions given. Permissions should be\n" "__ space-seperated, eg. ENTRY INTERACT MODIFY\n" "__ Leave it blank to set all permissions off.\n" "/acls <zone> add <user> <permission>\n" "__ Add a single permission to what a user can do.\n" "/acls <zone> remove <user> <permission>\n" "__ Remove a single permission from what a user can do.\n" "/acls <zone> clear <user>\n" "__ Reset the given user to use the default permissions.\n" "To modify the default permissions, replace <user> with EVERYONE.")
def on_packet(packet, user, to_server): if not to_server: return packet # ENTRY if packet.name() in ('Player position', 'Player position & look') and not check_acls(ENTRY, user): tell(user, "You are not allowed to enter here.", delay=0.2, lock=("acl-entry-lock",user)) new_stance = packet.data['stance'] - packet.data['y'] + user.position_old[1] packet.data['x'], packet.data['y'], packet.data['z'] = user.position_old packet.data['stance'] = new_stance back_packet = Packet() back_packet.ident = 0x0B # Player position back_packet.direction = SERVER_TO_CLIENT back_packet.data['x'] = packet.data['x'] back_packet.data['y'] = packet.data['y'] back_packet.data['z'] = packet.data['z'] back_packet.data['stance'] = packet.data['stance'] back_packet.data['on_ground'] = packet.data['on_ground'] send_packet(back_packet, user, False) return packet # MODIFY (block digging) if packet.name() == 'Player digging' and packet.data['status'] == 0: position = (packet.data['x'], packet.data['y'], packet.data['z']) if not check_acls(MODIFY, user, position): tell(user, "You are not allowed to dig here.") return [] # MODIFY/INTERACT (block placement) if packet.name() == 'Player block placement' and not all(packet.data[key] == -1 for key in ('x','y','z','direction')): position = add_direction((packet.data['x'], packet.data['y'], packet.data['z']), packet.data['direction']) if packet.data['slot']['id'] in INTERACT_IDS: if not check_acls(INTERACT, user, position): tell(user, "You are not allowed to interact with things here.") return [] else: if not check_acls(MODIFY, user, position): tell(user, "You are not allowed to place blocks here.") # For now, we tell the client that the block in the indicated direction is empty. # This should work almost all the time and is by far the easiest option. back_packet = Packet() back_packet.ident = 0x35 # Block change back_packet.direction = SERVER_TO_CLIENT back_packet.data['x'], back_packet.data['y'], back_packet.data['z'] = position back_packet.data['id'] = 0 back_packet.data['metadata'] = 0 send_packet(back_packet, user, False) # We also invalidate the packet rather than drop it, so the server auto-corrects the client's inventory packet.data['x'] += 100 return packet
def make_zone(user, name, bounds_info): zone = new_zone(name, bounds_info, user.username, confirmed=False) if zone is None: tell(user, "An error occurred while creating the zone. Try again or call an op.") else: tell(user, "Zone %s created. Exiting menu." % name) if user in ops(): zone['confirmed'] = True tell(user, "Zone auto-confirmed for ops.") else: tell(user, "You will need to get your zone approved by an op\n" "for plot protection to work.")
def helpinfo(message, user, command): helptext = { 'view': "If no zone given, shows a list of zone names.\n" "If a zone name given (and you have ADMIN rights to it)," "displays a bunch of information about the zone.\n" "Not all this info is likely to be useful, but oh well.", 'here': "List all zones that you are currently standing in.\n" "Also gives what permissions you are allowed in that zone.\n", 'destroy': "Delete a zone and all associated information.\n" "This includes any plot protection the zone may have provided.\n" "No, ops cannot undo this. Use only if you are really sure.", 'new': "When used with nothing else given, ie. just /zone new, " "opens menu that will make a new zone step by step.\n" "For advanced use, give the zone name followed by bounds args.\n" "Bounds definitions are one of the following:\n" "__ cube <dim> <x1> <y1> <z1> <x2> <y2> <z2>\n" "__ cylinder <dim> <x> <y> <z> <radius> <height>\n" "__ json <json> (EXPERIMENTAL, MAY NOT WORK)\n" "Note that <dim> should be 0,-1,1 for overworld, Nether, End\n" "The json bounds defn is special. The remainder of the line " "should be valid JSON. If you don't know what that is, ignore it. JSON schema " "is given by typing /zone help json", 'help': "Ha ha, you're really funny. If you're reading this\n" "you know how to use the help function already, moron.", 'json': "In these definitions, we use the syntax <name:type> or just <type>.\n" "point := [<x:float>, <y:float>, <z:float>]\n" "bounds_info :=\n" "__ ['cube', <dim:int>, <point1:point>, <point2:point>] OR\n" "__ ['cylinder', <dim:int>, <base:point>, <radius:float>,\n" "____ <height:float>] OR\n" "__ ['union', <bounds_info>, <bounds_info>, ...]\n" "For example, the following describes a zone that covers both a cylinder " "in Overworld and a cube in the Nether:\n" "['union', ['cylinder', 0, [128,64,-128], 8, 64], \n" "__ ['cube', -1, [64,64,-64], [80,128,-80]]]" } if command not in helptext: tell(user, "Unknown command: %s" % command) else: tell(user, helptext[command])
def tell_bounds(style, *args, **kwargs): indent = kwargs.pop("indent",0) indent_str = "__" * indent if style == 'cube': dim, point1, point2 = args tell(user, indent_str + "Cube in dim%d%s from (%.2f,%.2f,%.2f) to (%.2f,%.2f,%.2f)" % ((dim, dim_to_name(dim)) + tuple(point1) + tuple(point2))) elif style == 'cylinder': dim, base, radius, height = args tell(user, indent_str + "Cylinder in dim%d%s from (%.2f,%.2f,%.2f) to radius %.2f and height %.2f" % ((dim, dim_to_name(dim)) + tuple(base) + (radius, height))) elif style == 'union': tell(user, indent_str + "Union of regions:") for more_info in args: tell_bounds(*more_info, indent=indent+1) else: tell(user, indent_str + "Unknown region")
def acladd(message, user, zone, name, perm): if perm not in ALL_PERMS: tell(user, "%s not an ACL." % perm) current = zone['acls'].setdefault(name, zone['acls']['EVERYONE'][:]) if perm in current: tell(user, "%s can already %s" % (name, perm)) else: current.append(perm) tell(user, "Success")
def aclclear(message, user, zone, name): if name == 'EVERYONE': tell(user, "EVERYONE is not valid for this command") return if name not in zone['acls']: tell(user, "%s doesn't have any specific ACLs" % name) else: zone['acls'].pop(name) tell(user, "Success")
def aclrm(message, user, zone, name, perm): if perm not in ALL_PERMS: tell(user, "%s not an ACL." % perm) current = zone['acls'].setdefault(name, zone['acls']['EVERYONE'][:]) if perm not in current: tell(user, "%s already can't %s" % (name, perm)) else: current.remove(perm) tell(user, "Success")
def zonedestroy(message, user, name): name = name.strip('"') try: zone = get_zones()[name] except KeyError: tell(user, "That zone doesn't exist!") return if not controls_zone(user.username, zone) and not user.username in ops(): tell(user, "That isn't your zone.") get_zones().pop(name) tell(user, "Zone deleted.")
def zoneinfo(message, user, name): name = name.strip('"') try: zone = get_zones()[name] except KeyError: tell(user, "That zone doesn't exist!") return if not controls_zone(user.username, zone) and not user.username in ops(): tell(user, "That isn't your zone.") return keys = ['name','creator','confirmed'] for key in keys: if key in zone: tell(user, "%s: %s" % (key, zone[key])) keys.append('bounds_info') def dim_to_name(dim): try: return {-1: '(Nether)', 0: '(Overworld)', 1:'(End)'}[dim] except KeyError: return "" def tell_bounds(style, *args, **kwargs): indent = kwargs.pop("indent",0) indent_str = "__" * indent if style == 'cube': dim, point1, point2 = args tell(user, indent_str + "Cube in dim%d%s from (%.2f,%.2f,%.2f) to (%.2f,%.2f,%.2f)" % ((dim, dim_to_name(dim)) + tuple(point1) + tuple(point2))) elif style == 'cylinder': dim, base, radius, height = args tell(user, indent_str + "Cylinder in dim%d%s from (%.2f,%.2f,%.2f) to radius %.2f and height %.2f" % ((dim, dim_to_name(dim)) + tuple(base) + (radius, height))) elif style == 'union': tell(user, indent_str + "Union of regions:") for more_info in args: tell_bounds(*more_info, indent=indent+1) else: tell(user, indent_str + "Unknown region") tell_bounds(*zone['bounds_info']) if 'acls' in zone: keys.append('acls') tell(user, "acls:") for name in zone['acls']: if name == 'EVERYONE': continue tell(user, "__%s: %s" % (name, ",".join(zone['acls'][name]))) tell(user, "__Everyone else: %s" % ",".join(zone['acls']['EVERYONE'])) for key in zone: if key in keys: continue tell(user, "%s: %s" % (key, zone[key]))
def opforce(message, user, mode): user.acl_force = (mode == 'on') tell(user, "Set force %s" % mode)
def zonelist(message, user): for zone in get_zones().values(): if controls_zone(user.username, zone): tell(user, zone['name'] + (" (unconfirmed)" if not zone.get('confirmed',True) else ""))
def start_menu(message, user): try: display(user, *ROOT_MENU) except MenuLockError: tell(user, "You already have a menu open.\nTry :again to see it or :exit to quit it.")
def exit_menu(user, value): tell(user, "Zone creation cancelled")
def zonenew(message, user, name, args): name = name.strip('"') if name in get_zones(): tell(user, "Name already taken.") return if args.startswith("json "): try: data = loads(args[5:]) except Exception: tell(user, "Bad json.") return def validate_point(data): if type(data) != list: raise ValueError() if len(data) != 3: raise ValueError() if not all(type(x) in (int, float) for x in data): raise ValueError() def validate_info(data): if type(data) != list: raise ValueError() style = data[0] if style == 'cube': dim, point1, point2 = data[1:] if type(dim) != int or dim not in (-1,0,1): raise ValueError() validate_point(point1) validate_point(point2) elif style == 'cylinder': dim, base, radius, height = data[1:] if type(dim) != int or dim not in (-1,0,1): raise ValueError() if not validate_point(base): raise ValueError() if type(radius) not in (int, float) or radius <= 0: raise ValueError() if type(height) not in (int, float) or height <= 0: raise ValueError() elif style == 'union': [validate_info(x) for x in data[1:]] else: raise ValueError() try: validate_info(data) except ValueError: tell(user, "Invalid JSON, see /help json for schema.") return zone = new_zone(name, data, user.username) else: args = filter(None, args.split()) style = args.pop(0) try: dim = int(args.pop(0)) if dim not in (-1,0,1): raise ValueError() args = [float(arg) for arg in args] if style == 'cube': if len(args) != 6: raise ValueError() point1 = args[:3] point2 = args[3:] zone = new_zone(name, ('cube',dim,point1,point2), user.username) elif style == 'cylinder': if len(args) != 5: raise ValueError() base = args[:3] radius, height = args[3:] if radius <= 0 or height <= 0: raise ValueError() zone = new_zone(name, ('cylinder',dim,base,radius,height), user.username) else: raise ValueError except ValueError: tell(user, "Bad args.") return if zone is None: tell(user, "Unknown error while creating zone") else: tell(user, "Success. Remember that you still need an op to confirm the zone.")
def zoneconfirm(message, user, name): if name not in zones.get_zones(): tell(user, "Zone does not exist.") return zones.get_zones()[name]['confirmed'] = True tell(user, "Success")