-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
220 lines (190 loc) · 6.22 KB
/
main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/.
*
* The original code is copyright (c) 2022, open.mp team and contributors.
*/
// Required for most of open.mp.
#include <sdk.hpp>
// Include the pawn component information.
#include <Server/Components/Pawn/pawn.hpp>
// Include pawn-natives macros (`SCRIPT_API`) and lookups (`IPlayer&`).
#include <Server/Components/Pawn/pawn_natives.hpp>
// Include a few function implementations. Should only be included once.
#include <Server/Components/Pawn/pawn_impl.hpp>
// If this data is to be used in other components only share an ABI stable base class.
struct IPawnExtension : IExtension
{
// Visit https://open.mp/uid to generate a new unique ID (different to the component UID).
PROVIDE_EXT_UID(/* UID GOES HERE */);
// Just one example public method.
virtual void setData(int value) = 0;
};
// This is a player data extension. It is created when a player connects and destroyed when they
// disconnect. Like a component it also needs a UID, also get from https://open.mp/uid
class PawnExtension final : public IPawnExtension
{
private:
// Any data, this is just a normal class.
int data_ = 0;
public:
// Implement the public API.
void setData(int value) override
{
data_ = value;
}
// Add a function to this local instantiation of the public interface.
int getData() const
{
return data_;
}
// Implement the core of the extensions API.
void freeExtension() override
{
// Delete this extension.
delete this;
}
void reset() override
{
// Optionally reset data when the main mode changes.
data_ = 0;
}
};
// As with the extension this should use an abstract interface if it is to be passed to other
// components. Like the files in `<Server/Components/>` you would share only this base class and
// keep the implementation private.
class PawnTemplate final : public IComponent, public PawnEventHandler, public PlayerConnectEventHandler
{
private:
// Hold a reference to the main server core.
ICore* core_ = nullptr;
// Hold a reference to the pawn component so methods in it can be called.
IPawnComponent* pawn_ = nullptr;
public:
// Visit https://open.mp/uid to generate a new unique ID (different to the extension UID).
PROVIDE_UID(/* UID GOES HERE */);
// When this component is destroyed we need to tell any linked components this it is gone.
~PawnTemplate()
{
// Clean up what you did above.
if (pawn_)
{
pawn_->getEventDispatcher().removeEventHandler(this);
}
if (core_)
{
core_->getPlayers().getPlayerConnectDispatcher().removeEventHandler(this);
}
}
// Implement the pawn script listener API.
void onAmxLoad(IPawnScript& script) override
{
// Because we're using `SCRIPT_API` this call automatically registers the declared natives.
pawn_natives::AmxLoad(script.GetAMX());
}
void onAmxUnload(IPawnScript& script) override
{
}
// Implement the player connection events API.
void onPlayerConnect(IPlayer& player) override
{
// Allocate a new copy of the extension and register it for `queryExtension` lookups.
player.addExtension(new PawnExtension(), true);
}
// Implement the main component API.
StringView componentName() const override
{
return "Pawn Template";
}
SemanticVersion componentVersion() const override
{
return SemanticVersion(0, 0, 1, 0);
}
void onLoad(ICore* c) override
{
// Cache core, listen to player events.
core_ = c;
// Register this component as wanting to be informed when a player (dis)connects.
core_->getPlayers().getPlayerConnectDispatcher().addEventHandler(this);
core_->printLn("Pawn component template loaded.");
setAmxLookups(core_);
}
void onInit(IComponentList* components) override
{
// Cache components, add event handlers here.
pawn_ = components->queryComponent<IPawnComponent>();
if (pawn_)
{
// For the legacy `amx_` C API this call sets the correct pointers so that pawn
// function calls call the original versions within the server.
setAmxFunctions(pawn_->getAmxFunctions());
// For the pawn-natives system this call sets the various component references used for
// parameter value lookups.
setAmxLookups(components);
// Register this component as wanting to be informed when a script is loaded.
pawn_->getEventDispatcher().addEventHandler(this);
}
}
void onReady() override
{
// Fire events here at earliest.
}
void onFree(IComponent* component) override
{
// Invalidate pawn pointer so it can't be used past this point.
if (component == pawn_)
{
pawn_ = nullptr;
setAmxFunctions();
setAmxLookups();
}
}
void free() override
{
// Deletes the component.
delete this;
}
void reset() override
{
// Resets data when the mode changes.
}
};
// Automatically called when the compiled binary is loaded.
COMPONENT_ENTRY_POINT()
{
return new PawnTemplate();
}
// `SCRIPT_API` is an enhanced wrapper around the old *pawn-natives* system:
//
// https://github.com/Y-Less/pawn-natives
//
// `IPlayer` is a reference to a player already resolved from a `playerid` in the calling pawn code.
// Many components define lookups to convert from IDs to direct references to their entity
// instances, and players, arrays, strings, and more are all handled by default (including return
// values). This saves all the old native boilerplate required for converting to and from the
// `params` array.
SCRIPT_API(SetPawnData, bool(IPlayer& player, int value))
{
// Try get a reference to this player's custom data.
if (auto* data = queryExtension<IPawnExtension>(player))
{
// Call a method on the extension.
data->setData(value);
return true;
}
// Natives return `0`/`false` by default if parameter lookups fail.
return false;
}
// Note that `amx` and `params` are still available in these natives via `GetAMX` and `GetParams`
// and may be needed for vararg functions like `format` or `SetTimerEx`.
SCRIPT_API(GetPawnData, int(IPlayer& player))
{
// Try get a reference to this player's custom data.
if (auto* data = queryExtension<IPawnExtension>(player))
{
// Within this component we can use the private API as well.
return reinterpret_cast<PawnExtension*>(data)->getData();
}
return 0;
}