-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmessage.jai
146 lines (116 loc) · 4.73 KB
/
message.jai
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
#scope_export
MEMLEAK_CHECK :: 0;
/*
An object that can be serialized to a bitstream.
Messages are objects that are sent between client and server across the connection. They are carried inside the
ConnectionPacket generated by the Connection class. Messages can be sent reliable-ordered, or unreliable-unordered,
depending on the configuration of the channel they are sent over.
To use messages, create your own set of message structs by "inheriting" from this struct:
MyMessage :: struct {
#as base: Message;
some_field: int;
}
@see Connection
*/
Message :: struct {
id : u16; // The message id. For messages sent over reliable-ordered channels, this starts at 0 and increases with each message sent. For unreliable-unordered channels this is set to the sequence number of the packet the message was included in.
type : u16; // The message type. Corresponds to the type integer used when the message was created though the message factory.
blockData : [] u8; // The block data. Empty if no block is attached.
refCount : int;
}
message_acquire :: (message: *Message) -> *Message {
message.refCount += 1;
#if MEMLEAK_CHECK {
log("\nAcquired message % id % type %, refCount = %\n", message, message.id, message.type, message.refCount);
log_stack_trace(context.stack_trace, .VERBOSE_ONLY);
log("\n");
}
return message;
}
message_release :: (message: *Message) {
assert_greater(message.refCount, 0);
if message.refCount > 0 {
message.refCount -= 1;
}
#if MEMLEAK_CHECK {
log("\nReleased message % id % type %, refCount = %\n", message, message.id, message.type, message.refCount);
log_stack_trace(context.stack_trace, .VERBOSE_ONLY);
log("\n");
}
if message.refCount == 0 {
if message.blockData.count > 0 {
array_free(message.blockData);
}
free(message);
}
}
/*
A MessageFactory defines the set of message types that can be created.
User code should define an enum of all message types:
MessageType :: enum {
Connect;
Login;
InputCommand;
}
And define a serialize function (or one per message type) that can handle the serialization logic:
serialize :: (stream: *$S, message: *Message) -> bool {
if #complete cast(MessageType) message.type == {
case .Connect;
msg := cast(*MsgConnect) message;
serialize_int(stream, msg.foo);
case .Login;
msg := cast(*MsgLogin) message;
serialize_string(stream, msg.username);
serialize_string(stream, msg.password);
case .InputCommand;
msg := cast(*MsgInputCommand) message;
serialize_int(stream, msg.primaryAction);
serialize_int(stream, msg.secondaryAction);
}
return false;
}
As well as a router for the various kinds of streams:
// TODO(dlb): Make a macro that makes this part suck less for the user.
serialize_router :: (msgFactory: *MessageFactory, stream: *Stream, message: *Message) -> bool {
m := cast(*msgFactory.messages[message.id]) message;
if #complete stream.type == {
case .Write; return serialize(cast(*WriteStream ) stream, m);
case .Read; return serialize(cast(*ReadStream ) stream, m);
case .Measure; return serialize(cast(*MeasureStream) stream, m);
}
}
Then register those messages types and the router with the MessageFactory:
msgFactory: MessageFactory;
msgFactory.messages = .[MsgConnect, MsgLogin, MsgInputCommand];
msgFactory.serializeRouter = serialize_router;
*/
MessageFactory :: struct {
max_message_type: u16;
create: (messageType: u16) -> *Message;
serialize: (stream: *Stream, message: *Message) -> bool;
}
message_factory_create_message :: (messageFactory: MessageFactory, messageType: u16) -> *Message {
msg := messageFactory.create(messageType);
msg.type = messageType;
return msg;
}
#scope_module
/*
Helper function to serialize a block attached to a message. Used by channel implementations.
*/
serialize_message_block :: (stream: *Stream, message: *Message, maxBlockSize: int) -> bool {
isBlockMessage := stream.isWriting && message.blockData.count > 0;
serialize_bool(stream, *isBlockMessage);
if !isBlockMessage {
return true;
}
blockSize: int = ifx stream.isWriting then cast(int) message.blockData.count else 0;
serialize_int(stream, *blockSize, 1, maxBlockSize);
if stream.isReading {
message.blockData = NewArray(blockSize, u8);
}
serialize_bytes(stream, message.blockData);
return true;
}
#scope_file
#import "Serialize";