diff --git a/reapi/extra/amxmodx/scripting/include/reapi.inc b/reapi/extra/amxmodx/scripting/include/reapi.inc index ba2ad48..1616029 100644 --- a/reapi/extra/amxmodx/scripting/include/reapi.inc +++ b/reapi/extra/amxmodx/scripting/include/reapi.inc @@ -81,7 +81,8 @@ enum members_tables_e mt_csplayerweapon, mt_gib, mt_netadr, - mt_csentity + mt_csentity, + mt_netchan }; #define ReAPIFunc {EngineFunc, GamedllFunc, GamedllFunc_CBaseAnimating, GamedllFunc_CBasePlayer, GamedllFunc_CSGameRules, GamedllFunc_CGrenade, GamedllFunc_CWeaponBox, ReCheckerFunc, GamedllFunc_CBasePlayerWeapon, GamedllFunc_CGib, GamedllFunc_CBaseEntity, GamedllFunc_CBotManager} diff --git a/reapi/extra/amxmodx/scripting/include/reapi_engine.inc b/reapi/extra/amxmodx/scripting/include/reapi_engine.inc index b9bda12..be36a7d 100644 --- a/reapi/extra/amxmodx/scripting/include/reapi_engine.inc +++ b/reapi/extra/amxmodx/scripting/include/reapi_engine.inc @@ -30,6 +30,18 @@ native set_ucmd(const ucmd, const UCmd:var, any:...); */ native any:get_ucmd(const ucmd, const UCmd:var, any:...); +/* +* Sets netchan data. +* Use the net_* NetChan enum +*/ +native set_netchan(const index, const NetChan:var, any:...); + +/* +* Returns metchan data from an client. +* Use the net_* NetChan enum +*/ +native any:get_netchan(const index, const NetChan:var, any:...); + /* * Sets a NetAdr var. * @@ -184,7 +196,7 @@ native CheckVisibilityInOrigin(const ent, Float:origin[3], CheckVisibilityType:t /* * Sets the name of the map. * -* @param mapname New map name. +* @param mapname New map name. * * @noreturn */ @@ -228,19 +240,19 @@ native rh_reset_mapname(); native bool:rh_emit_sound2(const entity, const recipient, const channel, const sample[], Float:vol = VOL_NORM, Float:attn = ATTN_NORM, const flags = 0, const pitch = PITCH_NORM, emitFlags = 0, const Float:origin[3] = {0.0,0.0,0.0}); /* -* Forces an userinfo update. +* Forces an userinfo update * -* @param playerEntIndex Player entity index (starts from 1) +* @param index Client index * * @noreturn */ -native rh_update_user_info(playerEntIndex); +native rh_update_user_info(const index); /* * Kicks a client from server with message * -* @param index Client index -* @param message Message that will be sent to client when it is deleted from server +* @param index Client index +* @param message Message that will be sent to client when it is deleted from server * * @noreturn * @@ -261,12 +273,30 @@ native rh_get_net_from(output[], len); /* * Returns client's netchan playing time in seconds. * -* @param index Client index +* @param index Client index * -* @return Netchan connection time in seconds or 0 if client index is invalid or client is not connected +* @return Netchan connection time in seconds or 0 if client index is invalid or client is not connected */ native rh_get_client_connect_time(const index); +/* +* Checks if a specific entity is fully packed in a given frame for a host client. +* +* @param index Client index for whom we are checking the entity. +* @param entity Entity index to find in the table of entities for the given frame. +* @param frame Frame index where to look. Default is -1, which checks the previous frame. +* @note To check in the current frame, this native should be called at the end of the server frame. +* +* @return Returns true if the entity is fully packed and ready to be sent to all clients in the given frame, otherwise false. +*/ +native bool:rh_is_entity_fullpacked(const host, const entity, const frame = -1); + +/* +* Get real game time throughout the entire server lifecycle. +* +* @return Real game time +*/ +native Float:rh_get_realtime(); enum MessageHook { diff --git a/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc b/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc index 3025cc2..eee1f36 100644 --- a/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc +++ b/reapi/extra/amxmodx/scripting/include/reapi_engine_const.inc @@ -1191,96 +1191,96 @@ enum UCmd /* * Description: - * Member type: short - * Get params: get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, value); + * Get params: get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, value); */ ucmd_lerp_msec = BEGIN_MEMBER_REGION(usercmd), /* * Description: - * Member type: byte - * Get params: get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, value); + * Get params: get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, value); */ ucmd_msec, /* * Description: - * Member type: vec3_t - * Get params: get_ucmd(const ucmd, UserCmd:var, Float:output[3]); - * Set params: set_ucmd(const ucmd, UserCmd:var, Float:dest[3]); + * Get params: get_ucmd(const ucmd, UCmd:var, Float:output[3]); + * Set params: set_ucmd(const ucmd, UCmd:var, Float:dest[3]); */ ucmd_viewangles, /* * Description: - * Member type: float - * Get params: Float:get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, Float:value); + * Get params: Float:get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, Float:value); */ ucmd_forwardmove, /* * Description: - * Member type: float - * Get params: Float:get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, Float:value); + * Get params: Float:get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, Float:value); */ ucmd_sidemove, /* * Description: - * Member type: float - * Get params: Float:get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, Float:value); + * Get params: Float:get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, Float:value); */ ucmd_upmove, /* * Description: - * Member type: byte - * Get params: get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, value); + * Get params: get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, value); */ ucmd_lightlevel, /* * Description: - * Member type: unsigned short - * Get params: get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, value); + * Get params: get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, value); */ ucmd_buttons, /* * Description: - * Member type: byte - * Get params: get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, value); + * Get params: get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, value); */ ucmd_impulse, /* * Description: - * Member type: byte - * Get params: get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, value); + * Get params: get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, value); */ ucmd_weaponselect, /* * Description: - * Member type: int - * Get params: get_ucmd(const ucmd, UserCmd:var); - * Set params: set_ucmd(const ucmd, UserCmd:var, value); + * Get params: get_ucmd(const ucmd, UCmd:var); + * Set params: set_ucmd(const ucmd, UCmd:var, value); */ ucmd_impact_index, /* * Description: - * Member type: vec3_t - * Get params: get_ucmd(const ucmd, UserCmd:var, Float:output[3]); - * Set params: set_ucmd(const ucmd, UserCmd:var, Float:dest[3]); + * Get params: get_ucmd(const ucmd, UCmd:var, Float:output[3]); + * Set params: set_ucmd(const ucmd, UCmd:var, Float:dest[3]); */ ucmd_impact_position }; @@ -1322,6 +1322,134 @@ enum NetAdrVars netadr_port }; +/** +* enum NetSrc +*/ +enum NetSrc +{ + NS_CLIENT, + NS_SERVER, + NS_MULTICAST // xxxMO +}; + +/** +* enum NetChan +*/ +enum NetChan +{ + /* + * Description: NS_SERVER or NS_CLIENT, depending on channel + * Member type: int + * Get params: NetSrc:get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, NetSrc:value); + */ + net_sock = BEGIN_MEMBER_REGION(netchan), + + /* + * Description: Address this channel is talking to + * Member type: NetAdr + * Get params: NetAdr:get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, NetAdr:value); + */ + net_remote_address, + + /* + * Description: - + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_player_slot, + + /* + * Description: For timeouts. Time last message was received + * Member type: float + * Get params: Float:get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, Float:value); + */ + net_last_received, + + /* + * Description: Time when channel was connected + * Member type: float + * Get params: Float:get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, Float:value); + */ + net_connect_time, + + /* + * Description: Bandwidth choke. (Bytes per second) + * Member type: float + * Get params: Float:get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, Float:value); + */ + net_rate, + + /* + * Description: If rh_get_realtime() > cleartime, free to send next packet. + * Member type: float + * Get params: Float:get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, Float:value); + */ + net_cleartime, + + /* + * Description: A sequence number that increases with each incoming bunch of packets. + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_incoming_sequence, + + /* + * Description: The number of last outgoing message that has been ack'd. + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_incoming_acknowledged, + + /* + * Description: Single bit indicating the state of acknowledgment for the last reliable message. + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_incoming_reliable_acknowledged, + + /* + * Description: Single bit, maintained local that toggles between 0 and 1 to track the sequence of reliable messages received + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_incoming_reliable_sequence, + + /* + * Description: Message we are sending to remote + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_outgoing_sequence, + + /* + * Description: Whether the message contains reliable payload, single bit + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_reliable_sequence, + + /* + * Description: Outgoing sequence number of last send that had reliable data + * Member type: int + * Get params: get_netchan(const index, NetChan:var); + * Set params: set_netchan(const index, NetChan:var, value); + */ + net_last_reliable_sequence +}; + /** * Message argument types used with GetMessageArgType() */ diff --git a/reapi/src/member_list.cpp b/reapi/src/member_list.cpp index bf3a90d..8d1126a 100644 --- a/reapi/src/member_list.cpp +++ b/reapi/src/member_list.cpp @@ -29,6 +29,7 @@ #define PMOVE_MEMBERS(mx) STRUCT_MEMBERS(com_playermove, mx, pm_##mx) #define MOVEVAR_MEMBERS(mx) STRUCT_MEMBERS(movevars_t, mx, mv_##mx) #define UCMD_MEMBERS(mx) STRUCT_MEMBERS(usercmd_s, mx, ucmd_##mx) +#define NETCHAN_MEMBERS(mx) STRUCT_MEMBERS(netchan_t, mx, net_##mx) #define PMTRACE_MEMBERS(mx) STRUCT_MEMBERS(pmtrace_s, mx, pmt_##mx) #define NETADR_MEMBERS(mx) STRUCT_MEMBERS(netadr_t, mx, netadr_##mx) #define CSPL_MEMBERS(mx) CLASS_MEMBERS(CCSPlayer, mx, mx) @@ -115,6 +116,8 @@ inline MType getMemberType(ArmouryItemPack) { return MEMBER_INTEGER; } inline MType getMemberType(InfoMapBuyParam) { return MEMBER_INTEGER; } inline MType getMemberType(SecondaryAtkState) { return MEMBER_INTEGER; } inline MType getMemberType(netadrtype_t) { return MEMBER_INTEGER; } +inline MType getMemberType(netsrc_t) { return MEMBER_INTEGER; } +inline MType getMemberType(netadr_t) { return MEMBER_NETADR; } inline MType getMemberType(TraceResult) { return MEMBER_TRACERESULT; } @@ -753,6 +756,23 @@ member_t memberlist_pmtrace[] = { PMTRACE_MEMBERS(hitgroup), }; +member_t memberlist_netchan[] = { + NETCHAN_MEMBERS(sock), + NETCHAN_MEMBERS(remote_address), + NETCHAN_MEMBERS(player_slot), + NETCHAN_MEMBERS(last_received), + NETCHAN_MEMBERS(connect_time), + NETCHAN_MEMBERS(rate), + NETCHAN_MEMBERS(cleartime), + NETCHAN_MEMBERS(incoming_sequence), + NETCHAN_MEMBERS(incoming_acknowledged), + NETCHAN_MEMBERS(incoming_reliable_acknowledged), + NETCHAN_MEMBERS(incoming_reliable_sequence), + NETCHAN_MEMBERS(outgoing_sequence), + NETCHAN_MEMBERS(reliable_sequence), + NETCHAN_MEMBERS(last_reliable_sequence) +}; + member_t memberlist_csplayer[] = { CSPL_MEMBERS(m_szModel), CSPL_MEMBERS(m_bForceShowMenu), @@ -1094,6 +1114,7 @@ member_t *memberlist_t::operator[](size_t members) const CASE(movevars) CASE(usercmd) CASE(pmtrace) + CASE(netchan) CASE(csplayer) CASE(baseitem) CASE(baseweapon) diff --git a/reapi/src/member_list.h b/reapi/src/member_list.h index 4dc774e..23acfe2 100644 --- a/reapi/src/member_list.h +++ b/reapi/src/member_list.h @@ -26,6 +26,7 @@ enum MType MEMBER_PMTRACE, // struct pmtrace_t MEBMER_USERCMD, // struct usercmd_s MEMBER_TRACERESULT, // struct TraceResult + MEMBER_NETADR // struct netadr_t }; struct memberlist_t @@ -84,7 +85,8 @@ struct memberlist_t mt_csplayerweapon, mt_gib, mt_netadr, - mt_csentity + mt_csentity, + mt_netchan }; }; @@ -772,6 +774,24 @@ enum PMTrace pmt_hitgroup }; +enum NetChan +{ + net_sock = BEGIN_MEMBER_REGION(netchan), + net_remote_address, + net_player_slot, + net_last_received, + net_connect_time, + net_rate, + net_cleartime, + net_incoming_sequence, + net_incoming_acknowledged, + net_incoming_reliable_acknowledged, + net_incoming_reliable_sequence, + net_outgoing_sequence, + net_reliable_sequence, + net_last_reliable_sequence +}; + enum NetAdr { netadr_type = BEGIN_MEMBER_REGION(netadr), diff --git a/reapi/src/natives/natives_helper.h b/reapi/src/natives/natives_helper.h index 19fef5d..7ae53d8 100644 --- a/reapi/src/natives/natives_helper.h +++ b/reapi/src/natives/natives_helper.h @@ -2,12 +2,14 @@ #define PARAMS_COUNT (params[0] / sizeof(cell)) -#define CHECK_ISPLAYER(x) if (unlikely(params[x] <= 0 || params[x] > gpGlobals->maxClients)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid player index %i [%s]", __FUNCTION__, params[x], #x); return FALSE; } -#define CHECK_ISENTITY(x) if (unlikely(params[x] < 0 || params[x] > gpGlobals->maxEntities)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid entity index %i [%s]", __FUNCTION__, params[x], #x); return FALSE; } -#define CHECK_GAMERULES() if (unlikely(!g_pGameRules)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: gamerules not initialized", __FUNCTION__); return FALSE; } -#define CHECK_CONNECTED(x, y) if (unlikely(x == nullptr || x->has_disconnected)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[y]); return FALSE; } -#define CHECK_INSTANCE_OF(x, y) if (unlikely(dynamic_cast((x::BaseClass *)y) == nullptr)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid entity %d ('%s'), is not an instance of the base class '%s'", __FUNCTION__, indexOfEdict(y->pev), STRING(y->pev->classname), #x); return FALSE; } -#define CHECK_REQUIREMENTS(x) if (unlikely(!api_cfg.has##x())) { AMXX_LogError(amx, AMX_ERR_NATIVE, "Native '%s' is not available, %s required.", __FUNCTION__, #x); return FALSE; } if (!g_RehldsMessageManager) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: %s message manager not initialized.", __FUNCTION__, #x); return FALSE; } +#define CHECK_ISPLAYER(x) if (unlikely(params[x] <= 0 || params[x] > gpGlobals->maxClients)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid player index %i [%s]", __FUNCTION__, params[x], #x); return FALSE; } +#define CHECK_ISENTITY(x) if (unlikely(params[x] < 0 || params[x] > gpGlobals->maxEntities)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid entity index %i [%s]", __FUNCTION__, params[x], #x); return FALSE; } +#define CHECK_GAMERULES() if (unlikely(!g_pGameRules)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: gamerules not initialized", __FUNCTION__); return FALSE; } +#define CHECK_CONNECTED(x, y) if (unlikely(x == nullptr || x->has_disconnected)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[y]); return FALSE; } +#define CHECK_CLIENT_CONNECTED(x, y) if (unlikely(x == nullptr || !(x->active || x->spawned || x->connected))) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[y]); return FALSE; } +#define CHECK_APICLIENT_CONNECTED(x, y) if (unlikely(x == nullptr || !(x->IsActive() || x->IsSpawned() || x->IsConnected()))) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[y]); return FALSE; } +#define CHECK_INSTANCE_OF(x, y) if (unlikely(dynamic_cast((x::BaseClass *)y) == nullptr)) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: invalid entity %d ('%s'), is not an instance of the base class '%s'", __FUNCTION__, indexOfEdict(y->pev), STRING(y->pev->classname), #x); return FALSE; } +#define CHECK_REQUIREMENTS(x) if (unlikely(!api_cfg.has##x())) { AMXX_LogError(amx, AMX_ERR_NATIVE, "Native '%s' is not available, %s required.", __FUNCTION__, #x); return FALSE; } if (!g_RehldsMessageManager) { AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: %s message manager not initialized.", __FUNCTION__, #x); return FALSE; } class CAmxArg { diff --git a/reapi/src/natives/natives_members.cpp b/reapi/src/natives/natives_members.cpp index b80dbf4..1819010 100644 --- a/reapi/src/natives/natives_members.cpp +++ b/reapi/src/natives/natives_members.cpp @@ -670,6 +670,67 @@ cell AMX_NATIVE_CALL get_pmtrace(AMX *amx, cell *params) return get_member(amx, tr, member, dest, element); } +/* +* Sets netchan data. +* Use the net_* NetChan enum +* +* native set_netchan(const index, const NetChan:var, any:...); +*/ +cell AMX_NATIVE_CALL set_netchan(AMX *amx, cell *params) +{ + enum args_e { arg_count, arg_index, arg_var, arg_value }; + + CHECK_ISPLAYER(arg_index); + + client_t *pClient = clientOfIndex(params[arg_index]); + CHECK_CLIENT_CONNECTED(pClient, arg_index); + + member_t *member = memberlist[params[arg_var]]; + if (unlikely(member == nullptr)) { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: unknown member id %i", __FUNCTION__, params[arg_var]); + return FALSE; + } + + cell* value = getAmxAddr(amx, params[arg_value]); + return set_member(amx, &pClient->netchan, member, value, 0); +} + +/* +* Returns metchan data from an client. +* Use the net_* NetChan enum +* +* native any:get_netchan(const index, const NetChan:var, any:...); +*/ +cell AMX_NATIVE_CALL get_netchan(AMX *amx, cell *params) +{ + enum args_e { arg_count, arg_index, arg_var, arg_3 }; + + CHECK_ISPLAYER(arg_index); + + client_t *pClient = clientOfIndex(params[arg_index]); + CHECK_CLIENT_CONNECTED(pClient, arg_index); + + member_t *member = memberlist[params[arg_var]]; + if (unlikely(member == nullptr)) { + AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: unknown member id %i", __FUNCTION__, params[arg_var]); + return FALSE; + } + + cell* dest; + size_t element; + + if (PARAMS_COUNT == 3) { + dest = getAmxAddr(amx, params[arg_3]); + element = 0; + } + else { + dest = nullptr; + element = 0; + } + + return get_member(amx, &pClient->netchan, member, dest, element); +} + /* * Sets a NetAdr var. * @@ -824,6 +885,12 @@ AMX_NATIVE_INFO EngineVars_Natives[] = { "set_ucmd", set_ucmd }, { "get_ucmd", get_ucmd }, + { "set_netchan", set_netchan }, + { "get_netchan", get_netchan }, + + { "set_netadr", set_netadr }, + { "get_netadr", get_netadr }, + { "set_rebuy", set_rebuy }, { "get_rebuy", get_rebuy }, @@ -850,9 +917,6 @@ AMX_NATIVE_INFO ReGameVars_Natives[] = { "set_pmtrace", set_pmtrace }, { "get_pmtrace", get_pmtrace }, - { "set_netadr", set_netadr }, - { "get_netadr", get_netadr }, - { nullptr, nullptr } }; @@ -1125,6 +1189,8 @@ cell get_member(AMX *amx, void* pdata, const member_t *member, cell* dest, size_ return (cell)get_member_direct(pdata, member->offset, element); case MEBMER_USERCMD: return (cell)get_member_direct(pdata, member->offset, element); + case MEMBER_NETADR: + return (cell)get_member_direct(pdata, member->offset, element); default: break; } diff --git a/reapi/src/natives/natives_misc.cpp b/reapi/src/natives/natives_misc.cpp index 8c1ef8a..50300b9 100644 --- a/reapi/src/natives/natives_misc.cpp +++ b/reapi/src/natives/natives_misc.cpp @@ -3466,7 +3466,7 @@ AMX_NATIVE_INFO Misc_Natives_RG[] = /* * Sets the name of the map. * -* @param mapname New map name. +* @param mapname New map name. * * @noreturn * @@ -3579,16 +3579,23 @@ cell AMX_NATIVE_CALL rh_emit_sound2(AMX *amx, cell *params) ); } -// TODO: should we duplicate documentation for native here and in include? +/* +* Forces an userinfo update +* +* @param index Client index +* +* @noreturn +*/ cell AMX_NATIVE_CALL rh_update_user_info(AMX *amx, cell *params) { - enum args_e { arg_count, arg_playerEntIndex }; + enum args_e { arg_count, arg_index }; - CBasePlayer *pPlayer = getPrivate(params[arg_playerEntIndex]); - CHECK_CONNECTED(pPlayer, arg_playerEntIndex); + CHECK_ISPLAYER(arg_index); - CAmxArgs args(amx, params); - g_RehldsFuncs->SV_UpdateUserInfo(args[arg_playerEntIndex]); + IGameClient *pClient = clientByIndex(params[arg_index]); + CHECK_APICLIENT_CONNECTED(pClient, arg_index); + + g_RehldsFuncs->SV_UpdateUserInfo(pClient); return TRUE; } @@ -3596,8 +3603,8 @@ cell AMX_NATIVE_CALL rh_update_user_info(AMX *amx, cell *params) /* * Kicks a client from server with message * -* @param index Client index -* @param message Message that will be sent to client when it is deleted from server +* @param index Client index +* @param message Message that will be sent to client when it is deleted from server * * @noreturn * @@ -3609,15 +3616,11 @@ cell AMX_NATIVE_CALL rh_drop_client(AMX *amx, cell *params) CHECK_ISPLAYER(arg_index); - client_t *pClient = clientOfIndex(params[arg_index]); - if (unlikely(pClient == nullptr || !(pClient->active | pClient->spawned | pClient->connected))) - { - AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[arg_index]); - return FALSE; - } + IGameClient *pClient = clientByIndex(params[arg_index]); + CHECK_APICLIENT_CONNECTED(pClient, arg_index); char messagebuf[256]; - g_RehldsFuncs->DropClient(g_RehldsSvs->GetClient(params[arg_index] - 1), false, getAmxString(amx, params[arg_msg], messagebuf)); + g_RehldsFuncs->DropClient(pClient, false, getAmxString(amx, params[arg_msg], messagebuf)); return TRUE; } @@ -3643,12 +3646,27 @@ cell AMX_NATIVE_CALL rh_get_net_from(AMX* amx, cell* params) return TRUE; } +/* +* Get real game time throughout the entire server lifecycle. +* +* @return Real game time +* +* native Float:rh_get_realtime(); +*/ +cell AMX_NATIVE_CALL rh_get_realtime(AMX* amx, cell* params) +{ + enum args_e { arg_count }; + + float realtime = static_cast(g_RehldsFuncs->GetRealTime()); + return *(cell *)&realtime; +} + /* * Returns client's netchan playing time in seconds. * -* @param index Client index +* @param index Client index * -* @return Netchan connection time in seconds or 0 if client index is invalid or client is not connected +* @return Netchan connection time in seconds or 0 if client index is invalid or client is not connected * * native rh_get_client_connect_time(const index); */ @@ -3659,25 +3677,62 @@ cell AMX_NATIVE_CALL rh_get_client_connect_time(AMX *amx, cell *params) CHECK_ISPLAYER(arg_index); client_t *pClient = clientOfIndex(params[arg_index]); - if (unlikely(pClient == nullptr || !(pClient->active | pClient->spawned | pClient->connected))) - { - AMXX_LogError(amx, AMX_ERR_NATIVE, "%s: player %i is not connected", __FUNCTION__, params[arg_index]); - return FALSE; - } + CHECK_CLIENT_CONNECTED(pClient, arg_index); return (cell)(g_RehldsFuncs->GetRealTime() - pClient->netchan.connect_time); } +/* +* Checks if a specific entity is fully packed in a given frame for a host client. +* +* @param index Client index for whom we are checking the entity. +* @param entity Entity index to find in the table of entities for the given frame. +* @param frame Frame index where to look. Default is -1, which checks the previous frame. +* @note To check in the current frame, this native should be called at the end of the server frame. +* +* @return Returns true if the entity is fully packed and ready to be sent to all clients in the given frame, otherwise false. +* +* native bool:rh_is_entity_fullpacked(const host, const entity, const frame = -1); +*/ +cell AMX_NATIVE_CALL rh_is_entity_fullpacked(AMX *amx, cell *params) +{ + enum args_e { arg_count, arg_index, arg_entity, arg_frame }; + + const int SV_UPDATE_BACKUP = (gpGlobals->maxClients == 1) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; + const int SV_UPDATE_MASK = (SV_UPDATE_BACKUP - 1); + + CHECK_ISPLAYER(arg_index); + + client_t *pClient = clientOfIndex(params[arg_index]); + CHECK_CLIENT_CONNECTED(pClient, arg_index); + + int iEntity = params[arg_entity]; + int iFrame = params[arg_frame]; + + client_frame_t *frame = &pClient->frames[(pClient->netchan.outgoing_sequence + iFrame) & SV_UPDATE_MASK]; + packet_entities_t *fullpack = &frame->entities; + + for (int i = 0; i < fullpack->num_entities; i++) + { + const entity_state_t *es = &fullpack->entities[i]; + if (es->number == iEntity) + return TRUE; // GOTCHA! The given entity was found in the fullpack of entities + } + + return FALSE; +} + AMX_NATIVE_INFO Misc_Natives_RH[] = { - { "rh_set_mapname", rh_set_mapname }, - { "rh_get_mapname", rh_get_mapname }, - { "rh_reset_mapname", rh_reset_mapname }, - { "rh_emit_sound2", rh_emit_sound2 }, - { "rh_update_user_info", rh_update_user_info }, - { "rh_drop_client", rh_drop_client }, - { "rh_get_net_from", rh_get_net_from }, - + { "rh_set_mapname", rh_set_mapname }, + { "rh_get_mapname", rh_get_mapname }, + { "rh_reset_mapname", rh_reset_mapname }, + { "rh_emit_sound2", rh_emit_sound2 }, + { "rh_update_user_info", rh_update_user_info }, + { "rh_drop_client", rh_drop_client }, + { "rh_get_net_from", rh_get_net_from }, + { "rh_get_realtime", rh_get_realtime }, + { "rh_is_entity_fullpacked", rh_is_entity_fullpacked }, { "rh_get_client_connect_time", rh_get_client_connect_time }, { nullptr, nullptr }