Also precache model portraits (bmp)
[pmprecache.git] / dllapi.cpp
1 // vi: set ts=4 sw=4 :
2 // vim: set tw=75 :
3
4 /*
5 * Copyright (c) 2001-2003 Will Day <willday@hpgx.net>
6 *
7 * This file is part of Metamod.
8 *
9 * Metamod is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * Metamod is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with Metamod; if not, write to the Free Software Foundation,
21 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * In addition, as a special exception, the author gives permission to
24 * link the code of this program with the Half-Life Game Engine ("HL
25 * Engine") and Modified Game Libraries ("MODs") developed by Valve,
26 * L.L.C ("Valve"). You must obey the GNU General Public License in all
27 * respects for all of the code used other than the HL Engine and MODs
28 * from Valve. If you modify this file, you may extend this exception
29 * to your version of the file, but you are not obligated to do so. If
30 * you do not wish to do so, delete this exception statement from your
31 * version.
32 *
33 */
34
35 #include <unistd.h>
36
37 #include <extdll.h>
38
39 #include <dllapi.h>
40 #include <meta_api.h>
41
42 #define SAY(pEntity, text) {\
43 MESSAGE_BEGIN( MSG_ONE, GET_USER_MSG_ID(PLID, "SayText", NULL), NULL, pEntity );\
44 WRITE_BYTE( ENTINDEX(pEntity) );\
45 WRITE_STRING( text );\
46 MESSAGE_END();\
47 }
48
49 extern char gGamedir[];
50 int gmsgSayText;
51
52 #define MAXLENGTH_PLAYERNAME 64
53 #define MAXLENGTH_MODELNAME 64
54 #define MAXLENGTH_MODELPATH 256
55 typedef struct {
56 char model[MAXLENGTH_MODELPATH];
57 char bmp[MAXLENGTH_MODELPATH];
58 } precache_paths;
59
60 typedef struct {
61 char playername[MAXLENGTH_PLAYERNAME];
62 char modelname[MAXLENGTH_MODELNAME];
63 precache_paths paths;
64 } precache_entry;
65
66 #define MAX_PRECACHE_COUNT 32
67 precache_entry precache_list[MAX_PRECACHE_COUNT];
68 unsigned short int precache_count = 0;
69
70 bool fileExists(const char* path) {
71 char fullpath[256];
72 snprintf(fullpath, sizeof(fullpath), "%s/%s", &gGamedir[0], path);
73 return access(fullpath, R_OK) == 0;
74 }
75
76 bool addPrecacheEntry(const char* playername, const char* model) {
77 short int i = 0;
78 int streq = 1;
79 while ((i < precache_count) && ((streq = strcmp(precache_list[i].modelname, model)) != 0)) i++;
80 if ((i == precache_count) && (i < MAX_PRECACHE_COUNT) && (streq != 0)) {
81 strncpy(precache_list[i].playername, playername, MAXLENGTH_PLAYERNAME - 1);
82 strncpy(precache_list[i].modelname, model, MAXLENGTH_MODELNAME - 1);
83 // Ensure null-termination
84 precache_list[i].playername[MAXLENGTH_PLAYERNAME - 1] = 0;
85 precache_list[i].modelname[MAXLENGTH_MODELNAME - 1] = 0;
86
87 snprintf(precache_list[i].paths.model, MAXLENGTH_MODELPATH, "models/player/%s/%s.mdl", model, model);
88 snprintf(precache_list[i].paths.bmp, MAXLENGTH_MODELPATH, "models/player/%s/%s.bmp", model, model);
89
90 precache_count++;
91 LOG_MESSAGE(PLID, "Precache entry %d: playername=\"%s\" modelname=\"%s\" paths.model=\"%s\" paths.bmp=\"%s\"", i, precache_list[i].playername, precache_list[i].modelname, precache_list[i].paths.model, precache_list[i].paths.bmp);
92 return true;
93 }
94 else
95 return false;
96 }
97
98 void ClientPutInServer( edict_t *pEntity ) {
99 const char* playername = STRING(pEntity->v.netname);
100 char* modelname = g_engfuncs.pfnInfoKeyValue( g_engfuncs.pfnGetInfoKeyBuffer( pEntity ), "model" );
101 char modelfile[256];
102 snprintf(modelfile, sizeof(modelfile), "models/player/%s/%s.mdl", modelname, modelname);
103 LOG_MESSAGE(PLID, "Player %s is using model %s", playername, modelname);
104 if (fileExists( modelfile )) {
105 LOG_MESSAGE(PLID, "Model %s is present on server", modelname);
106 SAY(pEntity, "Your model is present on the server, it will be precached for the next round!");
107 if (addPrecacheEntry( playername, modelname ))
108 LOG_MESSAGE(PLID, "Added model %s to precache list", modelname);
109 else
110 LOG_MESSAGE(PLID, "Model %s will not be precached - reduntant or precache list is full", modelname);
111 }
112 else {
113 LOG_MESSAGE(PLID, "Unable to precache due to FILE MISSING: %s!", modelfile);
114 SAY(pEntity, "Your model is not present on the server, can't distribute for other players!");
115 }
116 RETURN_META(MRES_IGNORED);
117 }
118
119 void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) {
120 LOG_MESSAGE(PLID, "Precaching %d player models", precache_count);
121 for (int i = 0; i < precache_count; i++) {
122 LOG_MESSAGE(PLID, "Precaching model: %s (contributed by %s)", precache_list[i].modelname, precache_list[i].playername);
123 g_engfuncs.pfnPrecacheModel( precache_list[i].paths.model );
124 if (fileExists( precache_list[i].paths.bmp ))
125 {
126 LOG_MESSAGE(PLID, "Precaching bmp: %s", precache_list[i].paths.bmp);
127 g_engfuncs.pfnPrecacheGeneric( precache_list[i].paths.bmp );
128 }
129 else
130 LOG_MESSAGE(PLID, "File not found: %s", precache_list[i].paths.bmp);
131 }
132 precache_count = 0;
133 RETURN_META(MRES_HANDLED);
134 }
135
136 static DLL_FUNCTIONS gFunctionTable =
137 {
138 NULL, // pfnGameInit
139 NULL, // pfnSpawn
140 NULL, // pfnThink
141 NULL, // pfnUse
142 NULL, // pfnTouch
143 NULL, // pfnBlocked
144 NULL, // pfnKeyValue
145 NULL, // pfnSave
146 NULL, // pfnRestore
147 NULL, // pfnSetAbsBox
148
149 NULL, // pfnSaveWriteFields
150 NULL, // pfnSaveReadFields
151
152 NULL, // pfnSaveGlobalState
153 NULL, // pfnRestoreGlobalState
154 NULL, // pfnResetGlobalState
155
156 NULL, // pfnClientConnect
157 NULL, // pfnClientDisconnect
158 NULL, // pfnClientKill
159 ClientPutInServer, // pfnClientPutInServer
160 NULL, // pfnClientCommand
161 NULL, // pfnClientUserInfoChanged
162 ServerActivate, // pfnServerActivate
163 NULL, // pfnServerDeactivate
164
165 NULL, // pfnPlayerPreThink
166 NULL, // pfnPlayerPostThink
167
168 NULL, // pfnStartFrame
169 NULL, // pfnParmsNewLevel
170 NULL, // pfnParmsChangeLevel
171
172 NULL, // pfnGetGameDescription
173 NULL, // pfnPlayerCustomization
174
175 NULL, // pfnSpectatorConnect
176 NULL, // pfnSpectatorDisconnect
177 NULL, // pfnSpectatorThink
178
179 NULL, // pfnSys_Error
180
181 NULL, // pfnPM_Move
182 NULL, // pfnPM_Init
183 NULL, // pfnPM_FindTextureType
184
185 NULL, // pfnSetupVisibility
186 NULL, // pfnUpdateClientData
187 NULL, // pfnAddToFullPack
188 NULL, // pfnCreateBaseline
189 NULL, // pfnRegisterEncoders
190 NULL, // pfnGetWeaponData
191 NULL, // pfnCmdStart
192 NULL, // pfnCmdEnd
193 NULL, // pfnConnectionlessPacket
194 NULL, // pfnGetHullBounds
195 NULL, // pfnCreateInstancedBaselines
196 NULL, // pfnInconsistentFile
197 NULL, // pfnAllowLagCompensation
198 };
199
200 C_DLLEXPORT int GetEntityAPI2(DLL_FUNCTIONS *pFunctionTable,
201 int *interfaceVersion)
202 {
203 if(!pFunctionTable) {
204 UTIL_LogPrintf("GetEntityAPI2 called with null pFunctionTable");
205 return(FALSE);
206 }
207 else if(*interfaceVersion != INTERFACE_VERSION) {
208 UTIL_LogPrintf("GetEntityAPI2 version mismatch; requested=%d ours=%d", *interfaceVersion, INTERFACE_VERSION);
209 //! Tell metamod what version we had, so it can figure out who is out of date.
210 *interfaceVersion = INTERFACE_VERSION;
211 return(FALSE);
212 }
213 memcpy(pFunctionTable, &gFunctionTable, sizeof(DLL_FUNCTIONS));
214 return(TRUE);
215 }