egra: new window flags, some cosmetic fixes
[iv.d.git] / enet_test / dfmsq.d
blobb9dbcc4dea196128ceffaf1cb007c2b0a29bad9d
1 // query Doom2D:Forever master server
2 module dfmsq is aliced;
4 import iv.enet;
5 import iv.vfs.io;
8 enum NET_CH_MAIN = 0;
10 enum NET_MSG_LIST = "\xca"; // 202
13 struct ServerInfo {
14 char[256] ipstr = 0; // [0] is length
15 ushort port;
16 char[256] namestr = 0; // [0] is length
17 char[256] mapstr = 0; // [0] is length
18 ubyte modeval;
19 ubyte players;
20 ubyte maxplayers;
21 ubyte protover;
22 ubyte haspass;
24 @property const(char)[] ip () const pure nothrow @trusted @nogc { pragma(inline, true); return ipstr.ptr[1..1+cast(ubyte)ipstr.ptr[0]]; }
25 @property const(char)[] name () const pure nothrow @trusted @nogc { pragma(inline, true); return namestr.ptr[1..1+cast(ubyte)namestr.ptr[0]]; }
26 @property const(char)[] map () const pure nothrow @trusted @nogc { pragma(inline, true); return mapstr.ptr[1..1+cast(ubyte)mapstr.ptr[0]]; }
28 const(ubyte)[] parse (const(ubyte)[] data) {
29 if (data.length < 10) throw new Exception("out of data");
31 ubyte getu8 () {
32 if (data.length < 1) throw new Exception("out of data");
33 ubyte res = data.ptr[0];
34 data = data[1..$];
35 return res;
38 ushort getu16 () {
39 if (data.length < 2) throw new Exception("out of data");
40 ushort res = cast(ushort)(data.ptr[0]|(data.ptr[1]<<16));
41 data = data[2..$];
42 return res;
45 void getstr (char[] st) {
46 auto len = getu8();
47 st[] = 0;
48 st[0] = cast(char)len;
49 if (len > 0) {
50 if (data.length < len) throw new Exception("out of data");
51 st[1..1+len] = (cast(const(char)[])data)[0..len];
52 data = data[len..$];
56 getstr(ipstr[]);
57 port = getu16;
58 getstr(namestr[]);
59 getstr(mapstr[]);
60 modeval = getu8;
61 players = getu8;
62 maxplayers = getu8;
63 protover = getu8;
64 haspass = getu8;
66 return data;
69 @property string mode () const pure nothrow @trusted @nogc {
70 switch (modeval) {
71 case 0: return "unknown";
72 case 1: return "DM";
73 case 2: return "TDM";
74 case 3: return "CTF";
75 case 4: return "COOP";
76 case 5: return "SINGLE";
77 default: return "invalid";
81 void dump () {
82 writeln(name, " at ", ip, ":", port, ", map ", map, "; mode: ", mode, "; players: ", players, " of ", maxplayers, "; version is ", protover, "; protected: ", haspass);
87 ENetPeer *connectToServer (ENetHost* client) {
88 ENetAddress address;
89 ENetEvent event;
90 ENetPeer *peer;
92 enet_address_set_host(&address, "mpms.doom2d.org");
93 address.port = 25665;
94 // Initiate the connection, allocating the two channels 0 and 1.
95 peer = enet_host_connect(client, &address, 2, 0);
96 if (peer is null) throw new Exception("No available peers for initiating an ENet connection.");
98 // wait up to 5 seconds for the connection attempt to succeed.
99 if (enet_host_service(client, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) {
100 return peer;
103 // either the 5 seconds are up or a disconnect event was received
104 // reset the peer in the event the 5 seconds had run out without any significant event
105 enet_peer_reset(peer);
106 return null;
110 void runClient (bool compress) {
111 auto client = enet_host_create(
112 null, // create a client host
113 1, // only allow 1 outgoing connection
114 2, // allow up 2 channels to be used, 0 and 1
115 57600/8, // 56K modem with 56 Kbps downstream bandwidth
116 14400/8, // 56K modem with 14 Kbps upstream bandwidth
118 if (client is null) throw new Exception("An error occurred while trying to create an ENet client host.");
119 scope(exit) enet_host_destroy(client);
120 if (compress) enet_host_compress_with_range_coder(client);
122 writeln("connecting to server...");
123 auto peer = connectToServer(client);
124 if (peer is null) {
125 writeln("connection failed!");
126 return;
129 // create a reliable packet of size 1 with query
130 auto packet = enet_packet_create(NET_MSG_LIST.ptr, 1, ENET_PACKET_FLAG_RELIABLE|ENET_PACKET_FLAG_NO_ALLOCATE);
132 // Send the packet to the peer
133 enet_peer_send(peer, NET_CH_MAIN, packet);
135 mainloop: for (;;) {
136 ENetEvent event;
137 // wait up to 1000 milliseconds for an event
138 while (enet_host_service(client, &event, 1000) > 0) {
139 switch (event.type) {
140 case ENET_EVENT_TYPE_CONNECT:
141 // to client?!
142 writefln("WTF?! A new client connected from %s:%s.", event.peer.address.host, event.peer.address.port);
143 // store any relevant client information here
144 //event.peer.data = "Client information\0".dup.ptr;
145 enet_peer_reset(event.peer);
146 break mainloop;
147 case ENET_EVENT_TYPE_RECEIVE:
148 //writefln("A packet of length %s was received on channel %s.", event.packet.dataLength, event.channelID);
150 scope(exit) enet_packet_destroy(event.packet);
151 //write(" ["); for (int f = 0; f < event.packet.dataLength; ++f) write(cast(char)event.packet.data[f]); write("]\n");
152 // clean up the packet now that we're done using it
153 auto data = cast(const(ubyte)[])(event.packet.data[0..event.packet.dataLength]);
154 if (data.length < 2) {
155 writeln(" ERROR: received packed too small");
156 break mainloop;
158 if (data[0] != cast(ubyte)NET_MSG_LIST[0]) {
159 writeln(" ERROR: invalid reply received");
160 break mainloop;
162 int count = data[1];
163 writeln("server count: ", count);
164 data = data[2..$];
165 try {
166 foreach (immutable sidx; 0..count) {
167 ServerInfo si;
168 data = si.parse(data);
169 si.dump();
171 } catch (Exception e) {
172 writeln("FATAL: invalid packed received!");
173 break mainloop;
176 enet_peer_disconnect_later(peer, 0);
177 break;
178 case ENET_EVENT_TYPE_DISCONNECT:
179 writefln("disconnected.");
180 // reset the peer's client information
181 event.peer.data = null;
182 break mainloop;
183 default:
184 break;
191 void main (string[] args) {
192 enum compress = false;
193 if (enet_initialize() != 0) throw new Exception("An error occurred while initializing ENet.");
194 scope(exit) enet_deinitialize();
196 runClient(compress);