Sourcemod (or Mani?) Style Player Filtering

Discuss API design here.
necavi
Developer
Posts: 129
Joined: Wed Jan 30, 2013 9:51 pm

Sourcemod (or Mani?) Style Player Filtering

Postby necavi » Fri Nov 06, 2015 3:08 am

In developing the auth module I realized that, more than ever, a standardized method for filtering players in commands.

As my development history is primarily concentrated on Sourcemod I personally believe that we should copy their semantics, with a few of our own additions.

For those not familiar with Sourcemod's player filtering I will give a short tutorial:
  • If you have not specified a preceding character the target is considered to be a playername, example: add_permission necavi sp.auth.commands.add_permission
  • If you specify a # at the beginning of the string it is considered a userid (player index in our case), example: add_permission #5 sp.auth.commands.add_permission
  • If you specify a @ at the beginning of the string it is considered a filter, example: add_permission @all sp.auth.commands.add_permission


I have also added the extension of having @! automatically target the opposite of a normal filter.
I strongly believe that this is a very simple and powerful tool that every administration tool requires, the only question in my mind is whether to use SM's semantics or Mani's, which users may be more familiar with.

I have already implemented this function in a fairly complete manner here.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Sun Nov 15, 2015 1:09 pm

I think I would prefer SM style, myself.

Should we require full names or even allow for partials? What if someone uses special characters in their name?

I would also suggest we use userids, not indexes, since the "status" command shows the userid of each player.

As far as filters, what all filters will we be using? Will there be a need for a ! to specify "not"? If so, how do we handle multiple filters? For instance @ct!dead or @ct/alive
Image
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Sun Nov 15, 2015 5:40 pm

If we want to be mathematically correct, we would use set operations for filters, because we are actually working with sets. I think the only interesting operations are "union" and "complement". As the notation for unions I would simply use the plus sign (+) and for complements the minus sign (-). Here are a few examples how it could look like:

Example #1:

Code: Select all

@ct-dead

Meaning:

CTs - dead players = all living CTs


Example #2:

Code: Select all

@ct+t

Meaning:

CTs + Ts = all CTs and all Ts


Example #3:

Code: Select all

@bot+(ct-dead)

Meaning:

bots + (CTs - dead players) = all bots and all living CTs


Example #4:

Code: Select all

@bot+ct-dead

Meaning:

bots + CTs - dead players = all bots and all CTs minus all dead players = all living bots and all living CTs

I guess you get the idea. :)
necavi
Developer
Posts: 129
Joined: Wed Jan 30, 2013 9:51 pm

Postby necavi » Wed Jan 13, 2016 5:32 pm

It lacks the parentheses you mentioned, but I finally had the opportunity to set this up, unfortunately I can't decide which package it should go in. Filters.Players? A new module in Commands? (it is basically entirely used for command filtering) Anyway, here's the code.

Syntax: Select all

import re

set_matching = re.compile("(\+|\-|\@)([A-Za-z]+)")

players = {
"t": set((5, 6, 3)),
"ct": set((1, 2, 4)),
"me": set((1,))
}

while 1:
my_string = input("sp_myfilter ")
state = set()
for match in set_matching.findall(my_string):
if match[0] == "@":
state = players[match[1]].copy()
elif match[0] == "+":
state |= players[match[1]]
elif match[0] == "-":
state -= players[match[1]]

print(state)
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Thu Jan 14, 2016 3:25 pm

I think the filters.players module is the proper place. But what do you think about this one? It's a bit different than the original target. It completely discards the idea of using prefixes to indicate the type of the item (name, userid or filter). Instead only userids and filters are allowed, but you can mix them if you want. It also supports parentheses!

Syntax: Select all

import ast
import operator

def eval_filter(expr):
filters = {
't': set([5, 6, 3]),
'ct': set([1, 2, 4]),
'me': set([1]),
'dead': set([4]),
'bot': set([6, 2])
}

return _eval_node(ast.parse(expr, mode='eval').body, filters)

operators = {
ast.Add: operator.or_,
ast.Sub: operator.sub
}

def _eval_node(node, filters):
# Userid?
if isinstance(node, ast.Num):
# TODO: Convert node.n to index
return set([node.n])

# Filter?
if isinstance(node, ast.Name):
return filters[node.id]

# <left> <operator> <right>
if isinstance(node, ast.BinOp):
return operators[type(node.op)](
_eval_node(node.left, filters), _eval_node(node.right, filters))

raise TypeError('Unsupported node type: {}'.format(node))

# Test
while True:
print(eval_filter(raw_input('Input: ')))
Example:

Code: Select all

Input: ct+t
set([1, 2, 3, 4, 5, 6])
Input: ct+10+11
set([1, 2, 11, 4, 10])
necavi
Developer
Posts: 129
Joined: Wed Jan 30, 2013 9:51 pm

Postby necavi » Thu Jan 14, 2016 4:24 pm

I like it quite a bit! I almost argued in favour of names, then remembered just how bad of an idea it is to target players by name (especially partial name). I'm reasonably certain that most of the people (especially those who would target by name) who use these commands will use them through the admin menu, NOT through the console directly.

Return to “API Design”

Who is online

Users browsing this forum: No registered users and 13 guests