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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
// Copyright 2020-2022 The Defold Foundation
// Copyright 2014-2020 King
// Copyright 2009-2014 Ragnar Svensson, Christian Murray
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
#ifndef DMSDK_MESSAGE_H
#define DMSDK_MESSAGE_H
#include <stdint.h>
#include <string.h>
#include <dmsdk/dlib/hash.h>
#include <dmsdk/dlib/align.h>
/*# Message API documentation
* [file:<dmsdk/dlib/message.h>]
*
* Api for sending messages
*
* @document
* @name Message
* @namespace dmMessage
*/
namespace dmMessage
{
/*#
* Result enum
* @enum
* @name dmMessage::Result
* @member RESULT_OK = 0
* @member RESULT_SOCKET_EXISTS = -1
* @member RESULT_SOCKET_NOT_FOUND = -2
* @member RESULT_SOCKET_OUT_OF_RESOURCES = -3
* @member RESULT_INVALID_SOCKET_NAME = -4
* @member RESULT_MALFORMED_URL = -5
* @member RESULT_NAME_OK_SOCKET_NOT_FOUND = -6
*/
enum Result
{
RESULT_OK = 0,
RESULT_SOCKET_EXISTS = -1,
RESULT_SOCKET_NOT_FOUND = -2,
RESULT_SOCKET_OUT_OF_RESOURCES = -3,
RESULT_INVALID_SOCKET_NAME = -4,
RESULT_MALFORMED_URL = -5,
RESULT_NAME_OK_SOCKET_NOT_FOUND = -6,
};
/*#
* Socket handle
* @typedef
* @name HSocket
*/
typedef dmhash_t HSocket;
/*#
* URL specifying a sender/receiver of messages
* @note Currently has a hard limit of 32 bytes
* @note This struct is a part of the save file APi (see script_table.cpp)
* @struct
* @name dmMessage::URL
*/
struct URL
{
URL()
{
memset(this, 0, sizeof(URL));
}
HSocket m_Socket;
dmhash_t _reserved; // Reserved for sub component path
dmhash_t m_Path;
dmhash_t m_Fragment;
};
/*#
* Helper struct for parsing a string of the form "socket:path#fragment"
* @note The sizes do not include the null character. There is no null character since the dmMessage::ParseURL is non destructive.
* @typedef
* @name dmMessage::StringURL
* @member m_Socket [type: const char*] The socket
* @member m_SocketSize [type: uint32_t] The socket length
* @member m_Path [type: const char*] The path
* @member m_PathSize [type: uint32_t] The path length
* @member m_Fragment [type: const char*] The fragment
* @member m_FragmentSize [type: uint32_t] The fragment length
*/
struct StringURL
{
StringURL()
{
memset(this, 0, sizeof(StringURL));
}
const char* m_Socket;
uint32_t m_SocketSize;
const char* m_Path;
uint32_t m_PathSize;
const char* m_Fragment;
uint32_t m_FragmentSize;
};
/*#
* Resets the given URL to default values.
* @note Previously the URL wasn't reset in the constructor and certain calls to ResetURL might currently be redundant
* @name ResetUrl
* @param url [type: dmMessage::URL] URL to reset
*/
void ResetURL(URL* url);
/*#
* Get the message socket
* @name GetSocket
* @param url [type: dmMessage::URL] url
* @return socket [type: dmMessage::HSocket]
*/
HSocket GetSocket(const URL* url);
/*#
* Set the socket
* @name SetSocket
* @param url [type: dmMessage::URL] url
* @param socket [type: dmMessage::HSocket]
*/
void SetSocket(URL* url, HSocket socket);
/*#
* Tests if a socket is valid (not deleted).
* @name IsSocketValid
* @param socket [type: dmMessage::HSocket] Socket
* @return result [type: bool] if the socket is valid or not
*/
bool IsSocketValid(HSocket socket);
/*#
* Get socket name
* @name GetSocketName
* @param socket [type: dmMessage::HSocket] Socket
* @return name [type: const char*] socket name. 0 if it was not found
*/
const char* GetSocketName(HSocket socket);
/*#
* Get the message path
* @name GetPath
* @param url [type: dmMessage::URL] url
* @return path [type: dmhash_t]
*/
dmhash_t GetPath(const URL* url);
/*#
* Set the message path
* @name SetPath
* @param url [type: dmMessage::URL] url
* @param path [type: dmhash_t]
*/
void SetPath(URL* url, dmhash_t path);
/*#
* Get the message fragment
* @name GetFragment
* @param url [type: dmMessage::URL] url
* @return fragment [type: dmhash_t]
*/
dmhash_t GetFragment(const URL* url);
/*#
* Set the message fragment
* @name SetFragment
* @param url [type: dmMessage::URL] url
* @param fragment [type: dmhash_t]
*/
void SetFragment(URL* url, dmhash_t fragment);
/*#
* @struct
* @name Message
*/
struct Message;
/*#
* A callback for messages that needs cleanup after being dispatched. E.g. for freeing resources/memory.
* @typedef
* @name dmMMessage::MessageDestroyCallback
* @see #dmMessage::Post
*/
typedef void(*MessageDestroyCallback)(dmMessage::Message* message);
/*#
* Message data desc used at dispatch callback. When a message is posted,
* the actual object is copied into the sockets internal buffer.
* @struct
* @name Message
* @member m_Sender [type: dmMessage::URL] Sender uri
* @member m_Receiver [type: dmMessage::URL] Receiver uri
* @member m_Id [type: dmhash_t] Unique id of message
* @member m_UserData1 [type: uintptr_t] User data pointer
* @member m_UserData2 [type: uintptr_t] User data pointer
* @member m_Descriptor [type: uintptr_t] User specified descriptor of the message data
* @member m_DataSize [type: uint32_t] Size of message data in bytes
* @member m_Next [type: dmMessage::Message*] Ptr to next message (or 0 if last)
* @member m_DestroyCallback [type: dmMessage::MessageDestroyCallback] If set, will be called after each dispatch
* @member m_Data [type: uint8_t*] Payload
*/
struct Message
{
URL m_Sender; //! Sender uri
URL m_Receiver; //! Receiver uri
dmhash_t m_Id; //! Unique id of message
uintptr_t m_UserData1; //! User data pointer
uintptr_t m_UserData2; //! User data pointer
uintptr_t m_Descriptor; //! User specified descriptor of the message data
uint32_t m_DataSize; //! Size of message data in bytes
struct Message* m_Next; //! Ptr to next message (or 0 if last)
MessageDestroyCallback m_DestroyCallback; //! If set, will be called after each dispatch
uint8_t DM_ALIGNED(16) m_Data[0]; //! Payload
};
/**
* Post an message to a socket
* @note Message data is copied by value
* @name Post
* @param sender [type: dmMessage::URL*] The sender URL if the receiver wants to respond. 0x0 is accepted
* @param receiver [type: dmMessage::URL*] The receiver URL, must not be 0x0
* @param message_id [type: dmhash_t] Message id
* @param user_data1 [type: uintptr_t] User data that can be used when both the sender and receiver are known
* @param user_data2 [type: uintptr_t] User data that can be used when both the sender and receiver are known.
* @param descriptor [type: uintptr_t] User specified descriptor of the message data
* @param message_data [type: void*] Message data reference
* @param message_data_size [type: uint32_t] Message data size in bytes
* @param destroy_callback [type: dmMessage::MessageDestroyCallback] if set, will be called after each message dispatch
* @return RESULT_OK if the message was posted
*/
Result Post(const URL* sender, const URL* receiver, dmhash_t message_id, uintptr_t user_data1, uintptr_t user_data2,
uintptr_t descriptor, const void* message_data, uint32_t message_data_size, MessageDestroyCallback destroy_callback);
/** post a ddf message to a socket
* Post a DDF message to a socket. A helper wrapper for Post()'ing a DDF message
* @note Message data is copied by value
* @name PostDDF
* @tparam TDDFType [type: TDDFType] Must be a DDF type
* @param message [type: TDDFType*] Message data reference
* @param sender [type: dmMessage::URL*] The sender URL if the receiver wants to respond. 0x0 is accepted
* @param receiver [type: dmMessage::URL*] The receiver URL, must not be 0x0
* @param user_data1 [type: uintptr_t] User data that can be used when both the sender and receiver are known
* @param user_data2 [type: uintptr_t] User data that can be used when both the sender and receiver are known.
* @param destroy_callback [type: dmMessage::MessageDestroyCallback] if set, will be called after each message dispatch
* @return RESULT_OK if the message was posted
* @examples
*
* ```cpp
* // dmMessage::URL sender, receiver;
* // dmGameObject::HInstance go;
*
* dmGameSystemDDF::PlayAnimation msg;
* msg.m_Id = animation_id;
* msg.m_Offset = params.m_CursorStart;
* msg.m_PlaybackRate = params.m_PlaybackRate;
*
* dmMessage::Result result = dmMessage::Post(&msg, &sender, &receiver, (uintptr_t)go, 0, 0));
* ```
*/
template <typename TDDFType>
Result PostDDF(const TDDFType* message, const URL* sender, const URL* receiver, uintptr_t user_data1, uintptr_t user_data2, MessageDestroyCallback destroy_callback)
{
return Post(sender, receiver, message->m_DDFDescriptor->m_NameHash, user_data1, user_data2,
(uintptr_t)message->m_DDFDescriptor, message, sizeof(TDDFType), destroy_callback);
}
/*#
* Convert a string to a URL struct
* @note No allocation occurs, thus no cleanup is needed.
* @name ParseUrl
* @param uri [type: const char*] string of the format [socket:][path][#fragment]
* @param out [type: dmMessage::StringUrl] url in string format, must not be 0x0
* @return
* - RESULT_OK on success
* - RESULT_MALFORMED_URL if the uri could not be parsed
*/
Result ParseURL(const char* uri, StringURL* out_url);
};
#endif // DMSDK_MESSAGE_H