diff --git a/Documentation/Networking.md b/Documentation/Networking.md index 815aa773..5acda7b5 100644 --- a/Documentation/Networking.md +++ b/Documentation/Networking.md @@ -73,9 +73,9 @@ We'd like to streamline a lot of this further, so this may be subject to change. ## Event updates -This is pretty simple. The server will use the FTE QuakeWorld supported `SVC_CGAMEPACKET` packet type to network events. +The server will use the FTE QuakeWorld supported `SVC_CGAMEPACKET` packet type to network events. -An example of such an event is as follows, it can be called at any time: +An example of such an event is as follows, it can be called at any time, anywhere on the server: ``` WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); @@ -92,14 +92,6 @@ From there you can use the builtins `readbyte()`, `readshort()` etc. which are a There's a few different means of networking things to the server as a client. -### infokeys - -Clients can set their own custom infokey data at any time, using the `setinfo` console command. For example: `setinfo foo bar` will set the infokey `foo` to the value of `bar`. The server can then read any given infokey on a given client like so: `string value = infokey(someplayer, "foo");` - -The server can also assign its own infokeys to a player. These can be overriden by the client at any given time as well, **unless the server prefixes them** with an asterisk. Here is an example: `forceinfokey(somePlayer, "*team", "1");` - -infokeys are visible to all other clients. So be careful about the type of data you store in them. Storing passwords or other sensitive information is not recommended. - ### sendevent `sendevent()` is the builtin you call from the client game to communicate, reliably, to @@ -216,4 +208,82 @@ Whenever the client issues a `cmd` based command, say: `cmd say foobar` in conso ## ServerInfo -To be written. \ No newline at end of file +ServerInfo keys can be set by server admins, or the game-logic. Those keys are networked to clients inside the game, as well as outside of the game. They can be queried through tools like `qstat` or `GameSpy`. + +### Setting ServerInfo + +#### Console (admins/debugging) + +When in the console, you can set the key `foo` to the value `bar` like this: + +``` +serverinfo foo bar +``` + +#### Server-side code + +The same thing can be done in the **SSQC** side with this line of code: + +``` +forceinfokey(world, "foo", "bar"); +``` + +### Retrieving ServerInfo + +#### Console (Debugging) + +At any time, the server and clients can enter `serverinfo` by itself into the console, and it will print out all known ServerInfo keys. + +Some of the keys are set by the engine, noteworthy ones include `*bspversion` and `mapname`. I encourage you try it and see if there's any keys that seem interesting to you and to make note of them. + +#### Client/server-side code + +We have two builtins to query ServerInfo keys from the client-side. `serverkey` and `serverkeyfloat`. Their usage goes like this: + +``` +string maybeBar = serverkey("foo"); +float someNum = serverkeyfloat("maxclients"); +``` + +## UserInfo + +UserInfo keys are means of allowing clients (and the server, more on that later) to communicate bits of information about themselves to the server and all other clients. Much like **ServerInfo**. Those are networked regardless of whether another client is in the same **PVS**. + +### Setting UserInfo + +#### Console (clients) + +Clients can set their own custom infokey data at any time, using the `setinfo` console command. For example: `setinfo foo bar` will set the infokey `foo` to the value of `bar`. + +#### Server-side code + +The server can also assign its own infokeys to a player. These can be overriden by the client at any given time - **unless the server prefixes them** with an asterisk. Here is an example: `forceinfokey(somePlayer, "*team", "1");` + +Client infokeys are visible to all other clients. That's how most of the scoreboard is filled in with information. So be careful about the type of data you store in them. Storing passwords or other sensitive information is not recommended. + +### Retrieving UserInfo + +#### Client-side code + +On the client-side, if you wanted to query your own players' specific infokey value, you can query it like this: + +``` +float myTeam = getplayerkeyfloat(player_localnum, "*team"); +``` + +And if you want to query a specific player entity, this is perfectly valid: + +``` +string theirName = getplayerkey(playerEnty.entnum-1, "name"); +``` + +As to why we have to subtract 1 from an entity its entnum for this, is because the clients in the infokey table start at **0**, and player entities on the server start at **1**; *0 being reserved for `world`*. + +#### Server-side code + +The server can then read any given infokey on a given client like so: + +``` +string value = infokey(someplayer, "foo"); +float otherValue = infokeyf(someplayer, "somenumber"); +``` diff --git a/src/shared/NSItem.h b/src/shared/NSItem.h new file mode 100644 index 00000000..54659145 --- /dev/null +++ b/src/shared/NSItem.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifdef SERVER +/* PICKUP ITEMS */ +class NSItem:NSRenderableEntity +{ +public: + void NSItem(void); + + /* overrides */ + virtual void Spawned(void); + virtual void Touch(entity); + virtual void Respawn(void); + virtual void SpawnKey(string, string); + + virtual void SetItem(int i); + virtual void SetFloating(int); + virtual void PickupRespawn(void); + +private: + int m_iClip; + int m_iWasDropped; + + /* spawn keys */ + int m_iInvItem; + string m_sndAcquire; + string m_sndRespawn; + int m_bFloating; + bool m_bSpins; +}; +#endif \ No newline at end of file diff --git a/src/shared/NSItem.qc b/src/shared/NSItem.qc new file mode 100644 index 00000000..d8d04b2d --- /dev/null +++ b/src/shared/NSItem.qc @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2023 Vera Visions LLC. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +#ifdef SERVER +void NSItem::NSItem(void) +{ + m_iClip = 0i; + m_iWasDropped = 0i; + m_iInvItem = 0i; + m_sndAcquire = __NULL__; + m_sndRespawn = __NULL__; + m_bFloating = false; +} + +void +NSItem::Spawned(void) +{ + super::Spawned(); + + Sound_Precache(m_sndAcquire); + Sound_Precache(m_sndRespawn); +} + +void NSItem::Respawn(void) +{ + SetSolid(SOLID_TRIGGER); + SetOrigin(GetSpawnOrigin()); + botinfo = BOTINFO_WEAPON; + + if (m_bSpins) + modelflags = MF_ROTATE; + else + modelflags &= ~MF_ROTATE; + + /* At some points, the item id might not yet be set */ + if (GetSpawnModel()) { + SetModel(GetSpawnModel()); + } + + SetSize([-16,-16,0], [16,16,16]); + ReleaseThink(); + + if (!m_bFloating) { + DropToFloor(); + SetMovetype(MOVETYPE_TOSS); + } +} + +void +NSItem::SpawnKey(string strKey, string strValue) +{ + switch (strKey) { + case "inv_item": + m_iInvItem = ReadInt(strValue); + break; + case "snd_acquire": + m_sndAcquire = ReadString(strValue); + break; + case "snd_respawn": + m_sndRespawn = ReadString(strValue); + break; + case "modelflags": + modelflags = ReadFloat(strValue); + break; + case "spin": + m_bSpins = ReadBool(strValue); + break; + case "frame": + frame = ReadFloat(strValue); + break; + case "body": + m_iBody = ReadInt(strValue); + break; + default: + super::SpawnKey(strKey, strValue); + break; + } +} + +void NSItem::Touch(entity eToucher) +{ + if (eToucher.classname != "player") { + return; + } + + /* don't remove if AddItem fails */ + if (Weapons_AddItem((player)eToucher, m_iInvItem, m_iClip) == FALSE) { + return; + } + + Logging_Pickup(eToucher, this, __NULL__); + StartSoundDef(m_sndAcquire, CHAN_ITEM, true); + + UseTargets(eToucher, TRIG_TOGGLE, m_flDelay); + + if (real_owner || m_iWasDropped == 1 || cvar("sv_playerslots") == 1) { + Destroy(); + } else { + Disappear(); + ScheduleThink(PickupRespawn, 30.0f); + } +} + +void NSItem::SetItem(int i) +{ + m_iInvItem = i; + m_oldModel = Weapons_GetWorldmodel(m_iInvItem); + SetModel(GetSpawnModel()); + SetSize([-16,-16,0], [16,16,16]); +} + +void NSItem::SetFloating(int i) +{ + m_bFloating = i ? true : false; +} + +void +NSItem::PickupRespawn(void) +{ + Respawn(); + StartSoundDef(m_sndRespawn, CHAN_ITEM, true); +} +#endif diff --git a/src/shared/defs.h b/src/shared/defs.h index 6a1aacd2..ce06e7dd 100644 --- a/src/shared/defs.h +++ b/src/shared/defs.h @@ -64,6 +64,7 @@ string __fullspawndata; #include "NSMonster.h" #include "NSTalkMonster.h" #include "NSProjectile.h" +#include "NSItem.h" #include "NSSpraylogo.h" #include "../xr/defs.h" diff --git a/src/shared/include.src b/src/shared/include.src index 774d5902..8bf30820 100644 --- a/src/shared/include.src +++ b/src/shared/include.src @@ -16,6 +16,7 @@ NSNavAI.qc NSMonster.qc NSTalkMonster.qc NSProjectile.qc +NSItem.qc NSClient.qc NSClientSpectator.qc