/*----------------------------------------------------------------------------*- =============================== Y Sever Includes - Master Core ===============================Description: Handles distribution of control of code segments across all running scripts with YSI to avoid conflicts of checkpoint streamers etc and allow features like /commands di_ySlaying all commands on the server.Legal: Copyright (C) 2007 Alex "Y_Less" Cole This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.Version: 0.2Changelog: 06/10/09: Rewrote using states. Added remote function macros. Reduced the number of masters to a single one. It must have them all. Vastly simplified the master model - ending the master ends everything. 06/01/08: Added code to not pass data if no other script exists. 17/11/07: Added code for a script to remove itself from the global list. 11/10/07: Not first version but added documentation months late.Functions: Public: Master_Refresh - Updates properties on GMX. Master_RelLo - Claims master in low priority scripts. Master_RelHi - Claims master in high priority scripts. Core: Master_Add - Adds code to the system for control. Master_Master - Sets up the script. Master_@Master - Sets down the script (gives up control on end). Stock: - Static: - Inline: MasterC@heckProp - Checks if a script section is owned. MasterS@etMaster - Asserts ownership on a script section. API: -Callbacks: -Definitions: MASTER_LEVEL - High or low priority script. MAX_MASTERS - Number of code sections controllable. MAX_MASTER_NAME - Max length of a section name. YSIM_MASTER - Name of the masterid property.Enums: <unnamed> - Master commands.Macros: -Tags: -Variables: Global: _@ - ID of this script. Static: YSIG@S@MasterCount - Number of sections registered. YSIG@S@Masters - Psudo-bit array of sections controlled. YSIG@S@MasterData - Section registration data.Commands: -Compile options: -Operators: -Natives: C@ - Renaming of CallRemoteFunction for size reasons. G@ - Renaming of getproperty for size reasons. S@ - Renaming of setproperty for size reasons.-*----------------------------------------------------------------------------*/#if defined _YSI_SETUP_MASTER #endinput#endif#define _YSI_SETUP_MASTER#include <YSI/YSI_debug.own>#define YSIM_MASTER "M"#define YSIM_RETURN #R#define YSIM_CALLER #C#define YSI_NO_MASTER -1#if defined YSIM_USE new _@ = -1;#endif#if !defined MAX_MASTERS #define MAX_MASTERS 32#else #if MAX_MASTERS > 32 #undef MAX_MASTERS #define MAX_MASTERS 32 #endif#endifenum (+= 0x01000000){ E_MASTER_RELINQUISH = 0x01000000, E_MASTER_SET_MASTER, E_MASTER_NOT_MASTER, E_MASTER_ERROR}#define MASTER_COMM_MASK (0xFF000000)#define MASTER_DATA_MASK (0x00FFFFFF)#if !defined MASTER_LEVEL #define MASTER_LEVEL 0#endif#if MASTER_LEVEL != 0 forward Master_RelHi(master);#else forward Master_RelLo(master);#endif#if defined YSIM_USE forward Master_Refresh();#endifstatic stock YSI_g_sMasterCount, YSI_g_sMasters, YSI_g_sMasterData[MAX_MASTERS][4 char];native S@(id=0, const name[]="", value=cellmin, const string[]="") = setproperty;native G@(id=0, const name[]="", value=cellmin, string[]="") = getproperty;native C@(const function[], const format[], {Float,_}:...) = CallRemoteFunction;stock L@(ret){ setproperty(8, YSIM_RETURN, ret);}stock M@(){ return setproperty(8, YSIM_CALLER, _@);}stock N@(){ setproperty(8, YSIM_CALLER, YSI_NO_MASTER); return getproperty(8, YSIM_RETURN);}stock O@(){ setproperty(8, YSIM_CALLER, YSI_NO_MASTER);}/*----------------------------------------------------------------------------*-Function: Master_CheckPropParams: name[] - Section to check control of.Return: -Notes: Checks if a named code section is currently controlled.-*----------------------------------------------------------------------------*/#define Master_CheckProp(%1) \ (existproperty(8, (%1)) && getproperty(8, (%1)) != -1)/*----------------------------------------------------------------------------*-Function: Master_SetMasterParams: name[] - Section to assert control over.Return: -Notes: Sets this script as the master of a certain named code section.-*----------------------------------------------------------------------------*/#define Master_SetMaster(%1) \ setproperty(8, (%1), _@); \ CallLocalFunction((%1), "i", E_MASTER_SET_MASTER)/*----------------------------------------------------------------------------*-Function: Master_MasterParams: -Return: -Notes: Constructor. Gets the script a master ID.-*----------------------------------------------------------------------------*/Master_Master(){ CallRemoteFunction("Master_Refresh", ""); #if defined YSIM_USE if (existproperty(8, YSIM_MASTER)) { new masters = getproperty(8, YSIM_MASTER), i = 0; while (i != 32) { if (!(masters & (1 << i))) { _@ = i; masters |= 1 << i; break; } ++i; } if (i != 32) { setproperty(8, YSIM_MASTER, masters); } } else { _@ = 0; setproperty(8, YSIM_MASTER, 1); } DBGC1(if (_@ == YSI_NO_MASTER) printf("*** Internal Error! MasterID not assigned");); #endif return 1;}/*----------------------------------------------------------------------------*-Function: Master_@MasterParams: -Return: -Notes: Destructor. Hands over processing of things to another script. Now only passes over if another master exists.-*----------------------------------------------------------------------------*/Master_@Master(){ #if defined YSIM_USE if (_@ == -1) { return 0; } for (new i = 0; i != YSI_g_sMasterCount; ++i) { if (YSI_g_sMasters & (1 << i)) { setproperty(8, YSI_g_sMasterData[i], -1); CallLocalFunction(YSI_g_sMasterData[i], "i", E_MASTER_NOT_MASTER); } } CallRemoteFunction("Master_RelHi", "i", _@); CallRemoteFunction("Master_RelLo", "i", _@); for (new i = 0; i < YSI_g_sMasterCount; i++) { if (YSI_g_sMasters & (1 << i)) { if (Master_CheckProp(YSI_g_sMasterData[i])) { CallLocalFunction(YSI_g_sMasterData[i], "i", E_MASTER_RELINQUISH | _@); } } else { CallRemoteFunction(YSI_g_sMasterData[i], "i", E_MASTER_RELINQUISH | _@); } } setproperty(8, YSIM_MASTER, getproperty(8, YSIM_MASTER) & (~(1 << _@))); #endif return 1;}/*----------------------------------------------------------------------------*-Function: Master_RelHi Master_RelLoParams: master - Master relinquishing control.Return: -Notes: Asserts an alternate script as controlling certain aspects. Two functions so some scripts can be set as more likely than others to get control, RelHi is called before RelLo so RelHi scripts will get control before RelLo scripts get a chance.-*----------------------------------------------------------------------------*/#if defined YSIM_USE && !defined YSIM_CLIENT #if MASTER_LEVEL != 0 public Master_RelHi(master) #else public Master_RelLo(master) #endif { if (master == _@) { return; } for (new i = 0; i < YSI_g_sMasterCount; i++) { if (!Master_CheckProp(YSI_g_sMasterData[i])) { Master_SetMaster(YSI_g_sMasterData[i]); YSI_g_sMasters |= 1 << i; } } }#endif/*----------------------------------------------------------------------------*-Function: Master_AddParams: name[] - Name of the property, script and function for global reference.Return: -Notes: Adds a section of the code to the master processor for globalisation and possible local handling. Uses packed strings because I can.-*----------------------------------------------------------------------------*/stock Master_Add(name[]){ if (YSI_g_sMasterCount >= MAX_MASTERS) { return E_MASTER_ERROR; } YSI_g_sMasterData[YSI_g_sMasterCount][0] = (name[0] << 24) | (name[1] << 16) | (name[2] << 8); if (Master_CheckProp(name)) { ++YSI_g_sMasterCount; return E_MASTER_NOT_MASTER; } setproperty(8, name, _@); YSI_g_sMasters |= 1 << YSI_g_sMasterCount; ++YSI_g_sMasterCount; return E_MASTER_SET_MASTER;}/*----------------------------------------------------------------------------*-Function: Master_RefreashParams: -Return: -Notes: Resets properties relevant to this script on a gamemode change.-*----------------------------------------------------------------------------*/#if defined YSIM_USEpublic Master_Refresh(){ if (_@ != -1) { setproperty(8, YSIM_MASTER, getproperty(8, YSIM_MASTER) | (1 << _@)); #if !defined YSIM_CLIENT for (new i = 0; i < YSI_g_sMasterCount; i++) { if (YSI_g_sMasters & (1 << i)) Master_SetMaster(YSI_g_sMasterData[i]); } #endif }}#endif#if defined YSIM_USE #if defined YSIM_CLIENT // Defined the functions as remote calling stubs. Then rename the // following code with a name unlikely to ever be used and define the // functions as stock so the compiler removes all the code quietly. #define REMOTE_FUNC:%1[%2](%3) stock %1(%3)return S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%3),S@(8,YSIM_CALLER,YSI_NO_MASTER),G@(8,YSIM_RETURN);stock _@%1(%3) #define REMOTE_FUNC@P:%1[%2](%3)<%4> stock %1(%3)return S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%4),S@(8,YSIM_CALLER,YSI_NO_MASTER),G@(8,YSIM_RETURN);stock _@%1(%3) #define REMOTE_FUNC@C:%1[%2](%3) stock %1(%3)return M@(),C@(#%1"R",%2,%3),N@();stock _@%1(%3) #define REMOTE_FUNC@PC:%1[%2](%3)<%4> stock %1(%3)return M@(),C@(#%1"R",%2,%4),N@();stock _@%1(%3) #define REMOTE_VOID:%1[%2](%3) stock %1(%3)S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%3),S@(8,YSIM_CALLER,YSI_NO_MASTER);stock _@%1(%3) #define REMOTE_VOID@P:%1[%2](%3)<%4> stock %1(%3)S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%4),S@(8,YSIM_CALLER,YSI_NO_MASTER);stock _@%1(%3) #define REMOTE_VOID@C:%1[%2](%3) stock %1(%3)M@(),C@(#%1"R",%2,%3),O@();stock _@%1(%3) #define REMOTE_VOID@PC:%1[%2](%3)<%4> stock %1(%3)M@(),C@(#%1"R",%2,%4),O@();stock _@%1(%3) #define REMOTE_STUB:%1[%2](%3) %2 %1(%3){if(FALSE)printf("",%3);return 0;}stock _@%1(%3) #define REMOTE_NULL:%1[%2]() %2 %1()return 0;stock _@%1() #define REMOTE_REGISTER #define REMOTE_CONTROL #else // Some ugly-ass code (ugly ass-code). #define REMOTE_FUNC:%1[%2](%3) forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{S@(8,YSIM_RETURN,%1(%3));}%1(%3)<_YCM:n>return S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%3),S@(8,YSIM_CALLER,YSI_NO_MASTER),G@(8,YSIM_RETURN);%1(%3)<_YCM:y> #define REMOTE_FUNC@P:%1[%2](%3)<%4> forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{S@(8,YSIM_RETURN,%1(%4));}%1(%3)<_YCM:n>return S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%4),S@(8,YSIM_CALLER,YSI_NO_MASTER),G@(8,YSIM_RETURN);%1(%3)<_YCM:y> #define REMOTE_FUNC@C:%1[%2](%3) forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{L@(%1(%3));}%1(%3)<_YCM:n>return M@(),C@(#%1"R",%2,%3),N@();%1(%3)<_YCM:y> #define REMOTE_FUNC@PC:%1[%2](%3)<%4> forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{L@(%1(%4));}%1(%3)<_YCM:n>return M@(),C@(#%1"R",%2,%4),N@();%1(%3)<_YCM:y> #define REMOTE_VOID:%1[%2](%3) forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{%1(%3);}%1(%3)<_YCM:n>S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%3),S@(8,YSIM_CALLER,YSI_NO_MASTER);%1(%3)<_YCM:y> #define REMOTE_VOID@P:%1[%2](%3)<%4> forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{%1(%4);}%1(%3)<_YCM:n>S@(8,YSIM_CALLER,_@),C@(#%1"R",%2,%4),S@(8,YSIM_CALLER,YSI_NO_MASTER);%1(%3)<_YCM:y> #define REMOTE_VOID@C:%1[%2](%3) forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{%1(%3);}%1(%3)<_YCM:n>M@(),C@(#%1"R",%2,%3),O@();%1(%3)<_YCM:y> #define REMOTE_VOID@PC:%1[%2](%3)<%4> forward %1R(%3);public %1R(%3)<_YCM:n>{}public %1R(%3)<_YCM:y>{%1(%4);}%1(%3)<_YCM:n>M@(),C@(#%1"R",%2,%4),O@();%1(%3)<_YCM:y> #define REMOTE_STUB:%1[%2](%3) %2 %1(%3)<_YCM:n>{if(FALSE)printf("",%3);return 0;}%2 %1(%3)<_YCM:y> #define REMOTE_NULL:%1[%2]() %2 %1()<_YCM:n>return 0;%2 %1()<_YCM:y> #define REMOTE_REGISTER state _YCM:n;_YCM(Master_Add(#_YCM)); #define REMOTE_CONTROL forward _YCM(n);public _YCM(n){switch(n&MASTER_COMM_MASK){case E_MASTER_SET_MASTER:state _YCM:y;case E_MASTER_NOT_MASTER:state _YCM:n;}} #endif #define Master_Caller() \ ((G@(8, YSIM_CALLER) == -1) ? (_@) : (G@(8, YSIM_CALLER))) #define Master_Save(%1); \ %1 = Master_Caller();#else #define REMOTE_FUNC:%1[%2](%3) stock %1(%3) #define REMOTE_FUNC@P:%1[%2](%3)<%4> stock %1(%3) #define REMOTE_FUNC@C:%1[%2](%3) stock %1(%3) #define REMOTE_FUNC@PC:%1[%2](%3)<%4> stock %1(%3) #define REMOTE_VOID:%1[%2](%3) stock %1(%3) #define REMOTE_VOID@P:%1[%2](%3)<%4> stock %1(%3) #define REMOTE_VOID@C:%1[%2](%3) stock %1(%3) #define REMOTE_VOID@PC:%1[%2](%3)<%4> stock %1(%3) #define REMOTE_STUB:%1[%2](%3) %2 %1(%3) #define REMOTE_NULL:%1[%2]() %2 %1() #define Master_Caller() (YSI_NO_MASTER) #define Master_Save(%1); #define REMOTE_REGISTER #define REMOTE_CONTROL#endif#define REMOTE_FUNC@p REMOTE_FUNC@P#define REMOTE_FUNC@c REMOTE_FUNC@C#define REMOTE_FUNC@pc REMOTE_FUNC@PC#define REMOTE_FUNC@pC REMOTE_FUNC@PC#define REMOTE_FUNC@Pc REMOTE_FUNC@PC#define REMOTE_FUNC@cp REMOTE_FUNC@PC#define REMOTE_FUNC@cP REMOTE_FUNC@PC#define REMOTE_FUNC@Cp REMOTE_FUNC@PC#define REMOTE_FUNC@CP REMOTE_FUNC@PC#define REMOTE_VOID@p REMOTE_VOID@P#define REMOTE_VOID@c REMOTE_VOID@C#define REMOTE_VOID@pc REMOTE_VOID@PC#define REMOTE_VOID@pC REMOTE_VOID@PC#define REMOTE_VOID@Pc REMOTE_VOID@PC#define REMOTE_VOID@cp REMOTE_VOID@PC#define REMOTE_VOID@cP REMOTE_VOID@PC#define REMOTE_VOID@Cp REMOTE_VOID@PC#define REMOTE_VOID@CP REMOTE_VOID@PC