Using __call__ to execute a command on server/client

Discuss API design here.
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Using __call__ to execute a command on server/client

Postby BackRaw » Wed Jul 18, 2012 9:29 pm

How about something like that:

Syntax: Select all

# sp core module

import sp_C # just a name for the C++ core, don't know what the name is, so let's assume it's sp_C

class Server(sp_C.Server):
def __call__(self, *args, **kwargs):
# assuming args[0] is a command-string, like 'say "Hello, World"', server-side

cmd = args[0]

if cmd:
sp_C.ServerCommand(cmd) # or ForceServerCommand or whatever

server = Server() # since this would make much sense

class Player(sp_C.Player):
def __call__(self, *args, **kwargs):
# assuming args[0] is a command-string, like 'say "Hello, World"', client-side

cmd = args[0]

if cmd:
ExecuteCommand(cmd) # assuming ExecuteCommand executes a command on the client's console, don't know what that one is named, sorry.

# [addon_load and stuff ....]

Now an example:

Syntax: Select all

import sp
from events.decorator import event

@event
def map_start(game_event):
sp.server('say "The server changed the map to %s"' % game_event.GetString("mapname"))

@event
def player_hurt(game_event):
player = sp.Player(game_event.GetInt("userid"))

player.AddHealth(140) # assuming that works :D

player("echo Added 140 health !!!")
I know the example isn't the best at all, but I hope you get what I mean =) that would make things easier and it looks good IMO ;)

Let me know what you think :)

EDIT: We can use a second argument for forcing the server-command, btw.
User avatar
Ayuto
Project Leader
Posts: 2193
Joined: Sat Jul 07, 2012 8:17 am
Location: Germany

Postby Ayuto » Wed Jul 18, 2012 10:49 pm

Why do you define kwargs? :) Also, you can simply add another parameter before *args, so you will get a more clearer error, if you don't pass anything. With these changes I come up with this:

Syntax: Select all

>>> class _Server(object):
def __call__(self, command, *args):
command = command + ' ' + ' '.join(map(str, args))
# Execute command...
# Test:
print command

def __getattr__(self, command):
return lambda *args: self(command, *args)


>>> Server = _Server()
>>> Server('say 123')
say 123
>>> Server('say 123', 456)
say 123 456
>>> Server.say(123)
say 123
>>> Server.say('123', 456)
say 123 456

Also, I added __getattr__, so we are able to use console commands as functions. :)
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Wed Jul 18, 2012 10:54 pm

Ayuto wrote:Why do you define kwargs? :) Also, you can simply add another parameter before *args, so you will get a more clearer error, if you don't pass anything. With these changes I come up with this:

Syntax: Select all

>>> class _Server(object):
def __call__(self, command, *args):
command = command + ' ' + ' '.join(map(str, args))
# Execute command...
# Test:
print command

def __getattr__(self, command):
return lambda *args: self(command, *args)


>>> Server = _Server()
>>> Server('say 123')
say 123
>>> Server('say 123', 456)
say 123 456
>>> Server.say(123)
say 123
>>> Server.say('123', 456)
say 123 456

Also, I added __getattr__, so we are able to use console commands as functions. :)


Nice! Don't know about those kwargs, I just put them there because it's provided everywhere haha.
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Thu Jul 19, 2012 1:38 am

We do not want to clutter up the sp.py, so more than likely "nothing" will need directly imported from sp itself. Once we get more of this functionality wrapped, we'll look into setting up the Python API for it.

We are still in discussions about this, but it could also use a decorator, which would look something like this:

Syntax: Select all

from commands.registry import ServerCommand
from commands.registry import ClientCommand
from commands.registry import SayCommand

@ServerCommand('command_to_register', 'Description of the command')
def my_server_command_callback(args):
pass

@ClientCommand('command_to_register', 'Description of the command')
def my_client_command_callback(userid, args):
pass

@SayCommand('command_to_register', 'Description of the command')
def my_say_command_callback(userid, args):
pass
I will also be changing the name of the "events" decorator class here soon. So, be on the lookout for that update here soon, as it will break any addons using that class.

Satoon
Omega_K2
Senior Member
Posts: 227
Joined: Sat Jul 07, 2012 3:05 am
Location: Europe
Contact:

Postby Omega_K2 » Thu Jul 19, 2012 3:15 am

Yeah, decorators sound good but the normal methods for registering/unregistering should also be provided (for API use & on-the-fly registering)
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Thu Jul 19, 2012 3:43 am

Yup, they will be, just like the Event registering/unregistering. I'm actually working on the API for this already, but won't commit anything until we have actual functionality to go with it. I will also need an Auth API to go with this as well, but if it is not ready by the time this is able to be committed, I will just add in the authorization section after it has been completed.

Satoon
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Thu Jul 19, 2012 12:44 pm

satoon101 wrote:We do not want to clutter up the sp.py, so more than likely "nothing" will need directly imported from sp itself. Once we get more of this functionality wrapped, we'll look into setting up the Python API for it.

We are still in discussions about this, but it could also use a decorator, which would look something like this:

Syntax: Select all

from commands.registry import ServerCommand
from commands.registry import ClientCommand
from commands.registry import SayCommand

@ServerCommand('command_to_register', 'Description of the command')
def my_server_command_callback(args):
pass

@ClientCommand('command_to_register', 'Description of the command')
def my_client_command_callback(userid, args):
pass

@SayCommand('command_to_register', 'Description of the command')
def my_say_command_callback(userid, args):
pass
I will also be changing the name of the "events" decorator class here soon. So, be on the lookout for that update here soon, as it will break any addons using that class.

Satoon


You got it wrong ;)

I mean like you type into the server's or client's console directly, not registering any command. The core will then find out which way to choose, like the ES example

Syntax: Select all

# drop a player's weapon on hurt

import es

def player_hurt(ev):
es.sexec(ev["userid"], "drop")
es.cexec(ev["userid"], "echo Your weapon has been dropped because you got hurt.")
would become this:

Syntax: Select all

import core # maybe that's a better way, having all of the classes in one "core" module
from events.decorator import event

@event
def player_hurt(game_event):
player = core.Player(game_event.GetInt("userid"))

player("drop") # Player class chooses the "es.sexec way"
player("echo Your weapon has been dropped because you got hurt.") # Player class chooses the "es.cexec way"
Get it? :D

And for the decorators for registering any command, YEAH! :D
User avatar
satoon101
Project Leader
Posts: 2697
Joined: Sat Jul 07, 2012 1:59 am

Postby satoon101 » Sun Jul 22, 2012 2:37 am

For your given example, this does not sound like a good idea to me at all. We would have to store all available commands for each type within Source.Python itself. This is definitely something we are not willing to do. Player functionality, like that of playerlib, is something that has not even gotten to the design stage at this point.

Satoon
User avatar
BackRaw
Senior Member
Posts: 537
Joined: Sun Jul 15, 2012 1:46 am
Location: Germany
Contact:

Postby BackRaw » Sun Jul 22, 2012 7:30 pm

satoon101 wrote:For your given example, this does not sound like a good idea to me at all. We would have to store all available commands for each type within Source.Python itself. This is definitely something we are not willing to do. Player functionality, like that of playerlib, is something that has not even gotten to the design stage at this point.

Satoon


Okay then I'll wait until the Player functionality is working, so I can write a simple library example and then we can move on ;)

Return to “API Design”

Who is online

Users browsing this forum: No registered users and 18 guests