Page 1 of 1

help porting pawn plugin to py

Posted: Sat Jun 06, 2015 3:45 am
by nergal
hey guys, I'd thought I'd give s.p a shot by porting one of my old sm plugins to source.python!

This plugin is meant to turn weapons transparent so they're entirely in your face.

The idea is to be able to see the weapon without the weapon blocking your view!

this is the original code

Syntax: Select all

#include <sourcemod>
#include <tf2_stocks>
#include <morecolors>

#pragma semicolon 1
#pragma newdecls required

#define MXPLYR MAXPLAYERS+1


int iProPlayer[ MXPLYR ];

public Plugin myinfo =
{
name = "WepAlphanator",
author = "Assyrian/Nergal",
description = "makes weapons transparent",
version = "1.0",
url = "http://www.sourcemod.net/"
}

public void OnPluginStart()
{
RegConsoleCmd("sm_pro", CommandSetWepAlpha, "Weapon Alpha");
RegConsoleCmd("sm_angles", CommandInfo, "Weapon Angles");
RegAdminCmd("sm_adpro", CommandSetAdminWepAlpha, ADMFLAG_SLAY, "Admin Weapon Alpha");

HookEvent("player_spawn", Eventery);
HookEvent("post_inventory_application", Eventery);

for (int i = 1; i <= MaxClients; i++)
{
if ( !IsValidClient(i) ) continue;
OnClientPutInServer(i);
}
}
public void OnClientPutInServer( int client )
{
iProPlayer[client] = -1;
}

public Action CommandInfo(int client, int args)
{
float flVector[3]; GetClientEyeAngles(client, flVector);
CPrintToChat(client, "{green}Your Eye Angles: x:%f, y:%f, z:%f", flVector[0], flVector[1], flVector[2]);

GetClientEyePosition(client, flVector);
CPrintToChat(client, "{green}Your Eye Position: x:%f, y:%f, z:%f", flVector[0], flVector[1], flVector[2]);

GetEntPropVector(client, Prop_Data, "m_vecAbsOrigin", flVector);
CPrintToChat(client, "{green}Your Absolute Origin from Property Vector: x:%f, y:%f, z:%f", flVector[0], flVector[1], flVector[2]);

GetEntPropVector(client, Prop_Send, "m_vecMaxs", flVector);
CPrintToChat(client, "{green}Your Vector Max Size: x:%f, y:%f, z:%f", flVector[0], flVector[1], flVector[2]);

GetEntPropVector(client, Prop_Send, "m_vecMins", flVector);
CPrintToChat(client, "{green}Your Vector Min Size: x:%f, y:%f, z:%f", flVector[0], flVector[1], flVector[2]);

GetEntPropVector(client, Prop_Data, "m_angAbsRotation", flVector);
CPrintToChat(client, "{green}Your Absolute Angle Rotation: x:%f, y:%f, z:%f", flVector[0], flVector[1], flVector[2]);

GetEntPropVector(client, Prop_Data, "m_vecVelocity", flVector);
CPrintToChat(client, "{green}Your Velocity: x:%f, y:%f, z:%f", flVector[0], flVector[1], flVector[2]);

return Plugin_Continue;
}
public Action CommandSetWepAlpha(int client, int args)
{
if (args < 1)
{
ReplyToCommand(client, "[Alpha Weps] Usage: !pro <0-255>");
return Plugin_Handled;
}
char number[8]; GetCmdArg(1, number, sizeof(number));

int maxalpha = StringToInt(number);
iProPlayer[client] = maxalpha;
SetWeaponInvis(client, maxalpha);
CPrintToChat(client, "{green}You've Turned Your Weapon Transparent!");

return Plugin_Continue;
}
public Action CommandSetAdminWepAlpha(int client, int args)
{
if (args < 2)
{
ReplyToCommand(client, "[Alpha Weps] Usage: !adpro <target> <0-255>");
return Plugin_Handled;
}
char szTargetname[64]; GetCmdArg(1, szTargetname, sizeof(szTargetname));
char szNum[64]; GetCmdArg(2, szNum, sizeof(szNum));

int maxalpha = StringToInt(szNum);

char target_name[MAX_TARGET_LENGTH];
int target_list[MAXPLAYERS+1], target_count;
bool tn_is_ml;
if ( (target_count = ProcessTargetString(szTargetname, client, target_list, MAXPLAYERS, COMMAND_FILTER_ALIVE, target_name, sizeof(target_name), tn_is_ml)) <= 0 )
{
ReplyToTargetError(client, target_count);
return Plugin_Handled;
}
for (int i = 0; i < target_count; i++)
{
if ( IsValidClient(target_list[i]) && IsPlayerAlive(target_list[i]) )
{
SetWeaponInvis(target_list[i], maxalpha);
CPrintToChat(target_list[i], "{unusual}Your Weapon Is Transparent!");
}
}
return Plugin_Handled;
}
public Action Eventery(Event event, const char[] name, bool dontBroadcast)
{
int i = GetClientOfUserId( event.GetInt("userid") );
if ( IsValidClient(i) )
{
if ( iProPlayer[i] != -1 ) SetWeaponInvis( i, iProPlayer[i] );
}
return Plugin_Continue;
}
stock void SetWeaponInvis(int client, int &alpha)
{
for (int i = 0; i < 5; i++)
{
int entity = GetPlayerWeaponSlot(client, i);
if ( IsValidEdict(entity) && IsValidEntity(entity) )
{
if (alpha < 0) alpha = 0;
else if (alpha > 255) alpha = 255;
SetEntityRenderMode(entity, RENDER_TRANSCOLOR);
SetEntityRenderColor(entity, 150, 150, 150, alpha);
}
}
return;
}
stock bool IsValidClient(int client, bool replaycheck = true)
{
if ( client <= 0 || client > MaxClients) return false;
if ( !IsClientInGame(client) ) return false;
if ( GetEntProp(client, Prop_Send, "m_bIsCoaching") ) return false;
if ( replaycheck ) if ( IsClientSourceTV(client) || IsClientReplay(client) ) return false;
return true;
}


and here's the ported code I somewhat made and copypasta'd some code from

Syntax: Select all

# Python Imports
#from collections import defaultdict

# SourcePython imports
from players.entity import PlayerEntity

from players.helpers import userid_from_index
from players.helpers import index_from_userid

from weapons.entity import WeaponEntity

from entities.constants import RenderMode

from events import Event
from colors import Color

from commands.say import SayCommand

from cvars.public import PublicConVar
from plugins.info import PluginInfo

info = PluginInfo()
info.name = "AlphaWeps"
info.author = "Nergal/Assyrian"
info.version = "1.0"
info.basename = "alphaweps"
info.variable = info.basename + "_version"
info.convar = PublicConVar(info.variable, info.version, 0, info.name + " Version")

#create a list
iProPlayer = [i+1 for i in range(66)]

@SayCommand('!pro')
def pro_command(index, teamonly, CCommand):
text = CCommand[1].split()
if len(text) < 0:
return True

iProPlayer[index] = int(text)
set_weapon_invis(userid_from_index(index), iProPlayer[index])

@Event
def player_connect(game_event):
userid = game_event.get_int('userid')
set_playerlist(userid)

@Event
def player_disconnect(game_event):
userid = game_event.get_int('userid')
set_playerlist(userid)

@Event
def player_spawn(game_event):
eventery(game_event.get_int('userid'))

@Event
def post_inventory_application(game_event):
eventery(game_event.get_int('userid'))

def eventery(userid):
index = index_from_userid(userid)
if iProPlayer[index] is not None:
set_weapon_invis(userid, iProPlayer[userid])

def set_playerlist(userid):
index = index_from_userid(userid)
iProPlayer[index] = None

def set_weapon_invis(userid, alpha):
try:
index = index_of_userid(userid)
except ValueError:
print('Wrong userid: ', userid)

player = PlayerEntity(index)
alpha = max(min(alpha, 255), 0)

for weapon_index in player.weapon_indexes():
entity = WeaponEntity(weapon_index)
entity.render_mode = RenderMode.TRANS_COLOR
entity.color = Color(150, 150, 150, alpha)


The plugin all loads fine but when I try out the command, it doesn't do anything but make me look stupid typing it in with everybody else typing it themselves (lol)

what did I do wrong?

Posted: Sat Jun 06, 2015 7:06 am
by Predz
From what I can see most of it is correct the only error I can notice is that PlayerEntity requires an index and not a userid. PlayerEntity(<index>)

Posted: Sat Jun 06, 2015 8:17 am
by BackRaw
FYI you don't need things like this in SP as SP raises an exception if the userid is invalid:

Syntax: Select all

#def is_client_valid(userid)
# index = index_from_userid(userid)
# if index < 0 or index >
If however you wanted to do the check anyway:

Syntax: Select all

# .. in some function providing a userid:
try:
index = index_of_userid(userid)
except ValueError:
print('Wrong userid: ', userid)

Posted: Sat Jun 06, 2015 1:31 pm
by L'In20Cible
BackRaw wrote:FYI you don't need things like this in SP as SP raises an exception if the userid is invalid:

Syntax: Select all

#def is_client_valid(userid)
# index = index_from_userid(userid)
# if index < 0 or index >
If however you wanted to do the check anyway:

Syntax: Select all

# .. in some function providing a userid:
try:
index = index_of_userid(userid)
except ValueError:
print('Wrong userid: ', userid)


Or: userid = index_from_userid(userid, raise_exception=False);if index == INVALID_ENTITY_INDEX: # wrong userid

Posted: Sat Jun 06, 2015 3:20 pm
by BackRaw
L'In20Cible wrote:
BackRaw wrote:FYI you don't need things like this in SP as SP raises an exception if the userid is invalid:

Syntax: Select all

#def is_client_valid(userid)
# index = index_from_userid(userid)
# if index < 0 or index >
If however you wanted to do the check anyway:

Syntax: Select all

# .. in some function providing a userid:
try:
index = index_of_userid(userid)
except ValueError:
print('Wrong userid: ', userid)


Or: userid = index_from_userid(userid, raise_exception=False);if index == INVALID_ENTITY_INDEX: # wrong userid

Wow that's cool :D

Posted: Sat Jun 06, 2015 4:59 pm
by nergal
BackRaw wrote:
L'In20Cible wrote:
BackRaw wrote:FYI you don't need things like this in SP as SP raises an exception if the userid is invalid:

Syntax: Select all

#def is_client_valid(userid)
# index = index_from_userid(userid)
# if index < 0 or index >
If however you wanted to do the check anyway:

Syntax: Select all

# .. in some function providing a userid:
try:
index = index_of_userid(userid)
except ValueError:
print('Wrong userid: ', userid)


Or: userid = index_from_userid(userid, raise_exception=False);if index == INVALID_ENTITY_INDEX: # wrong userid

Wow that's cool :D


the is_client_valid code, which is in the Pawn version above, is to check for client validity wholesale.

in Pawn, there was multiple functions that had a different idea of what way a client was valid.

Some needed to know if the client index was 1 or below-equal to max clients, some needed to know if the client was simply in game, some required to know if the player wasn't coaching, and others wanted to know if the players are bots and give the option to the function to exclude bots.

Posted: Sat Jun 06, 2015 6:07 pm
by nergal
tried the plugin, still didn't work >_>

updated OP with latest py code

Posted: Sat Jun 06, 2015 7:17 pm
by satoon101
Are you getting any errors in the server console? Are you using the latest release version or the newest repo version of SP?

Posted: Sat Jun 06, 2015 10:45 pm
by nergal
no errors, and yes, using the latest of s.p :3

Posted: Sun Jun 07, 2015 12:23 am
by satoon101
If you mean newest release (and not newest repo version), then I am not 100% certain that setting color works in that version. I know that getting the color raised an error. We will post another release soon, since there have been some really good changes since the last one.

Posted: Sun Jun 07, 2015 2:42 pm
by BackRaw
satoon101 wrote:If you mean newest release (and not newest repo version), then I am not 100% certain that setting color works in that version. I know that getting the color raised an error. We will post another release soon, since there have been some really good changes since the last one.


Setting color doesn't work in the May 21 release if I remember correctly.

Re: help porting pawn plugin to py

Posted: Thu Dec 15, 2016 12:47 am
by nergal
not to bring up this dinosaur thread but after a bit of time, I thought I'd try again with source.python since, from my perspective, alot of it seemed overhauled.

I took another stab at this "pro" plugin this. I attempted to update it to use the latest with source.python but the weapon transparency portion still does NOT work.

Syntax: Select all

# Python Imports
from collections import defaultdict

# SourcePython imports
from players.helpers import userid_from_index, index_from_userid, index_from_playerinfo, userid_from_playerinfo

from players._base import Player

from entities.constants import RenderMode
from events import Event

from weapons.entity import Weapon

from commands.say import SayCommand

from cvars.public import PublicConVar
from plugins.info import PluginInfo


info = PluginInfo()
info.name = "AlphaWeps"
info.author = "Nergal/Assyrian"
info.version = "1.0"
info.basename = "alphaweps"
info.variable = info.basename + "_version"
info.convar = PublicConVar(info.variable, info.version, info.name + " Version", 0)

#create a dictionary
dict_wep_invis_player = defaultdict(lambda: None) #dict(i+1 for i in range(65))

def load():
print("alphaweps is loaded!")

def unload():
print("alphaweps is unloaded!")


from commands.typed import TypedSayCommand
@TypedSayCommand('!pro')
def on_wepinvis_pro(command_info, x:int): # command_info.index is the only player info available
player_userid = userid_from_index(command_info.index)
set_weapon_invis(player_userid, x)

@TypedSayCommand('!vm')
def on_wepinvis_vm(command_info, x:int):
player_userid = userid_from_index(command_info.index)
set_weapon_invis(player_userid, x)

@Event('player_disconnect')
def on_player_disconnect(game_event):
if index in dict_wep_invis_player:
del dict_wep_invis_player[str(game_event['userid'])]

@Event('player_spawn')
def on_player_spawn(game_event):
make_weps_invis(game_event['userid'])

@Event('post_inventory_application')
def on_resupply(game_event):
make_weps_invis(game_event['userid'])

def make_weps_invis(userid):
if dict_wep_invis_player[str(userid)] is not None:
set_weapon_invis(userid, dict_wep_invis_player[str(userid)])

def set_weapon_invis(userid, ialpha):
player = Player.from_userid(userid)
ialpha = max( min(ialpha, 255), 0 )
print("set_weapon_invis called!")
dict_wep_invis_player[str(userid)] = ialpha
#print(dict_wep_invis_player[str(userid)])
#print(userid)
for weapon in player.weapon_indexes():
#wep = Weapon(weapon)
#wep.render_mode = RenderMode.TRANS_COLOR
Weapon(weapon).set_color(Color(255, 255, 255, ialpha))

Re: help porting pawn plugin to py

Posted: Thu Dec 15, 2016 5:02 am
by satoon101
First, I am assuming this is still for TF2. So, if not, please let us know. During my testing, I notice that there are no weapons being iterated over in player.weapon_indexes . This is because we don't have any weapon data for TF2. I will try to work a bit on this soon, but not sure when I will exactly have the time. If you want to help do that, please take a look at the weapon data we have for other games, and see if you can create the data for TF2 (game name is tf) and make a pull request:
https://github.com/Source-Python-Dev-Te ... on/weapons

Having said that, I have a few pointers from the last version of your plugin.

You still seem to be treating dictionaries like they're some sort of arrays. You don't have to have a value for every player at all times. For this plugin, there is really no need at all for a defaultdict. A regular dict will do just fine.

Your player_disconnect event references an 'index', when there is no such variable defined in that function. Also, you are (correctly) storing the values by userid, not the player's index.

Since, both player_spawn and post_inventory_application events do the exact same thing, you can declare them at the same time.

My only major issue with your plugin is that you import Player from players._base. That module is private because the players.entity module is designed to import the correct Player class from either _base, or the engine/game-specific class depending on whether an engine/game-specific module exists. Please ONLY import Player from players.entity.

We have recently added a Player.weapons() method to iterate over all of the player's weapons as Weapon instances. So, instead of using weapon_indexes and getting a Weapon instance, just use the new method. Player.weapons() does exactly the same thing already, so it saves you a bit on code.

Instead of calling Weapon.set_color() directly, I would recommend using the Weapon.color property.

Also, when setting the color, you can use the old RGB values and just change the alpha but using the current value of Weapon.color and calling the Color.with_alpha() method.

All-in-all, with the above pieces of advice, the plugin could look something like:

Syntax: Select all

# SourcePython imports
from commands.typed import TypedSayCommand
from core import echo_console
from cvars.public import PublicConVar
from events import Event
from listeners import LevelInit
from players.entity import Player
from plugins.info import PluginInfo


info = PluginInfo()
info.name = "AlphaWeps"
info.author = "Nergal/Assyrian"
info.version = "1.0"
info.basename = "alphaweps"
info.variable = info.basename + "_version"
info.convar = PublicConVar(info.variable, info.version, info.name + " Version", 0)


player_dict = {}


def load():
echo_console("alphaweps is loaded!")


def unload():
echo_console("alphaweps is unloaded!")


@TypedSayCommand('!pro')
def pro_command(command_info, x:int):
player = Player(command_info.index)
alpha = player_dict[player.userid] = max(min(x, 255), 0)
set_weapon_visibility(player.userid)


@TypedSayCommand('!vm')
def on_wepinvis_vm(command_info, x:int):
player = Player(command_info.index)
alpha = player_dict[player.userid] = max(min(x, 255), 0)
set_weapon_visibility(player.userid)


@Event('player_disconnect')
def on_player_disconnect(game_event):
userid = game_event['userid']
if userid in dict_wep_invis_player:
del dict_wep_invis_player[userid]


@Event('post_inventory_application', 'player_spawn')
def on_resupply(game_event):
echo_console(game_event.name)
set_weapon_visibility(game_event['userid'])


@LevelInit
def level_init(mapname):
player_dict.clear()


def set_weapon_visibility(userid):
alpha = player_dict.get(userid)
if alpha is None:
return

player = Player.from_userid(userid)
for weapon in player.weapons():
weapon.color = weapon.color.with_alpha(alpha)

Re: help porting pawn plugin to py

Posted: Thu Dec 15, 2016 5:11 pm
by nergal
satoon101 wrote:First, I am assuming this is still for TF2. So, if not, please let us know. During my testing, I notice that there are no weapons being iterated over in player.weapon_indexes . This is because we don't have any weapon data for TF2. I will try to work a bit on this soon, but not sure when I will exactly have the time. If you want to help do that, please take a look at the weapon data we have for other games, and see if you can create the data for TF2 (game name is tf) and make a pull request:
https://github.com/Source-Python-Dev-Te ... on/weapons

Having said that, I have a few pointers from the last version of your plugin.

You still seem to be treating dictionaries like they're some sort of arrays. You don't have to have a value for every player at all times. For this plugin, there is really no need at all for a defaultdict. A regular dict will do just fine.

Your player_disconnect event references an 'index', when there is no such variable defined in that function. Also, you are (correctly) storing the values by userid, not the player's index.

Since, both player_spawn and post_inventory_application events do the exact same thing, you can declare them at the same time.

My only major issue with your plugin is that you import Player from players._base. That module is private because the players.entity module is designed to import the correct Player class from either _base, or the engine/game-specific class depending on whether an engine/game-specific module exists. Please ONLY import Player from players.entity.

We have recently added a Player.weapons() method to iterate over all of the player's weapons as Weapon instances. So, instead of using weapon_indexes and getting a Weapon instance, just use the new method. Player.weapons() does exactly the same thing already, so it saves you a bit on code.

Instead of calling Weapon.set_color() directly, I would recommend using the Weapon.color property.

Also, when setting the color, you can use the old RGB values and just change the alpha but using the current value of Weapon.color and calling the Color.with_alpha() method.

All-in-all, with the above pieces of advice, the plugin could look something like:

Syntax: Select all

# SourcePython imports
from commands.typed import TypedSayCommand
from core import echo_console
from cvars.public import PublicConVar
from events import Event
from listeners import LevelInit
from players.entity import Player
from plugins.info import PluginInfo


info = PluginInfo()
info.name = "AlphaWeps"
info.author = "Nergal/Assyrian"
info.version = "1.0"
info.basename = "alphaweps"
info.variable = info.basename + "_version"
info.convar = PublicConVar(info.variable, info.version, info.name + " Version", 0)


player_dict = {}


def load():
echo_console("alphaweps is loaded!")


def unload():
echo_console("alphaweps is unloaded!")


@TypedSayCommand('!pro')
def pro_command(command_info, x:int):
player = Player(command_info.index)
alpha = player_dict[player.userid] = max(min(x, 255), 0)
set_weapon_visibility(player.userid)


@TypedSayCommand('!vm')
def on_wepinvis_vm(command_info, x:int):
player = Player(command_info.index)
alpha = player_dict[player.userid] = max(min(x, 255), 0)
set_weapon_visibility(player.userid)


@Event('player_disconnect')
def on_player_disconnect(game_event):
userid = game_event['userid']
if userid in dict_wep_invis_player:
del dict_wep_invis_player[userid]


@Event('post_inventory_application', 'player_spawn')
def on_resupply(game_event):
echo_console(game_event.name)
set_weapon_visibility(game_event['userid'])


@LevelInit
def level_init(mapname):
player_dict.clear()


def set_weapon_visibility(userid):
alpha = player_dict.get(userid)
if alpha is None:
return

player = Player.from_userid(userid)
for weapon in player.weapons():
weapon.color = weapon.color.with_alpha(alpha)



Yea it's still for tf2.

dictionaries ARE a type of array though :P
I practiced data structures by making one from scratch in C

Syntax: Select all

struct keyval {
const char *key;
void *val;
struct keyval *next;
};

struct dict {
unsigned int size; /* size of the pointer table */
unsigned int count; /* number of elements stored */
struct keyval **table; /* dictionaries are practically vectors of linked lists */
};

unsigned long gethash(const char *szKey)
{
unsigned const char *us;
unsigned long h = 0;
for (us = (unsigned const char *)szKey ; *us ; us++)
h = h * 97 + *us;
return h;
}

from what I learned and remembered, dictionaries combine linked lists and arraylists aka vectors using a keyed hash table to make an index number.

the old player_disconnect you guys used to have defined used an index but it's better that it's a userid now.

player_spawn and post_inventory_application both sorta do the same thing albeit with slight difference.
player_spawn is only called when the player exactly spawns. post_inventory_application is called when the player spawns and resupplies.

This is what i learned when I was doing SourcePawn crap.

I've used python alot in the past though I do regret not becoming a little more advanced with it which is why I'm going to be using source.python :)

as for the Color property, I do not need to set the render mode am I righte?

Re: help porting pawn plugin to py

Posted: Sun Dec 18, 2016 9:22 am
by Ayuto
nergal wrote:as for the Color property, I do not need to set the render mode am I righte?

Nope, you don't need to set it. That is done by SP behind the scenes.

Re: help porting pawn plugin to py

Posted: Mon Dec 19, 2016 7:27 am
by nergal
Ayuto wrote:
nergal wrote:as for the Color property, I do not need to set the render mode am I righte?

Nope, you don't need to set it. That is done by SP behind the scenes.


oh nice, another aspect is concerning C or C++ extensions. I'm already assuming that I can make a C-based py module and use that as a source.python plugin or no>?

Re: help porting pawn plugin to py

Posted: Mon Dec 19, 2016 7:36 am
by Ayuto
Yep, that would work. :smile: