1 /**
2 Copyright: Copyright (c) 2014-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module netlib.baseclient;
8 
9 import cbor;
10 import core.thread;
11 import std.conv : to;
12 import std.string : toStringz;
13 import voxelman.log;
14 
15 import derelict.enet.enet;
16 
17 import netlib;
18 
19 abstract class BaseClient
20 {
21 	mixin PacketManagement!(true);
22 	mixin BaseConnection!();
23 
24 	ENetAddress serverAddress;
25 	ENetPeer* server;
26 	bool isConnecting;
27 	bool isConnected;
28 
29 	void start(ConnectionSettings settings)
30 	{
31 		if (isRunning) stop();
32 
33 		host = enet_host_create(settings.address,
34 			settings.maxPeers,
35 			settings.numChannels,
36 			settings.incomingBandwidth,
37 			settings.outgoingBandwidth);
38 
39 		if (host is null)
40 		{
41 			error("An error occured while trying to create an ENet host");
42 			return;
43 		}
44 
45 		isRunning = true;
46 	}
47 
48 	void connect(string address, ushort port)
49 	{
50 		ENetAddress addr;
51 		enet_address_set_host(&addr, address.toStringz);
52 		addr.port = port;
53 
54 		if (isConnecting)
55 		{
56 			if (addr == serverAddress)
57 			{
58 				return;
59 			}
60 			else
61 			{
62 				disconnect();
63 			}
64 		}
65 		serverAddress = addr;
66 		connect();
67 	}
68 
69 	void connect()
70 	{
71 		server = enet_host_connect(host, &serverAddress, 2, 42);
72 		//enet_peer_timeout(server, 0, 0, 5000);
73 
74 		if (server is null)
75 		{
76 			error("An error occured while trying to create an ENet server peer");
77 			return;
78 		}
79 
80 		isConnecting = true;
81 	}
82 
83 	void disconnect()
84 	{
85 		if (isConnecting)
86 		{
87 			enet_peer_disconnect_now(server, 0);
88 			isConnecting = false;
89 		}
90 		else
91 		{
92 			enet_peer_disconnect(server, 0);
93 		}
94 	}
95 
96 	void send(ubyte[] data, ubyte channel = 0)
97 	{
98 		if (!isRunning) return;
99 		ENetPacket* packet = enet_packet_create(data.ptr, data.length,
100 				ENET_PACKET_FLAG_RELIABLE);
101 		enet_peer_send(server, channel, packet);
102 	}
103 
104 	void send(P)(auto ref const(P) packet, ubyte channel = 0)
105 		if (is(P == struct))
106 	{
107 		if (packetId!P >= packetArray.length)
108 		{
109 			infof("Dropping packet %s: %s", P.stringof, packetId!P);
110 			return;
111 		}
112 
113 		send(createPacket(packet), channel);
114 	}
115 
116 	// Set id mapping for packets
117 	void setPacketMap(string[] packetNames)
118 	{
119 		import std.algorithm : countUntil, remove, SwapStrategy;
120 
121 		PacketInfo*[] newPacketArray;
122 		newPacketArray.reserve(packetNames.length);
123 
124 		static bool pred(PacketInfo* packetInfo, string packetName)
125 		{
126 			return packetInfo.name == packetName;
127 		}
128 
129 		foreach(i, packetName; packetNames)
130 		{
131 			ptrdiff_t index = countUntil!pred(packetArray, packetName);
132 			size_t newId = newPacketArray.length;
133 
134 			if (index > -1)
135 			{
136 				newPacketArray ~= packetArray[index];
137 				remove!(SwapStrategy.unstable)(packetArray, index);
138 			}
139 			else
140 			{
141 				newPacketArray ~= new PacketInfo(packetName);
142 			}
143 			newPacketArray[$-1].id = newId;
144 		}
145 
146 		packetArray = newPacketArray;
147 	}
148 
149 	void update()
150 	{
151 		ENetEvent event;
152 		while (enet_host_service(host, &event, 0) > 0)
153 		{
154 			final switch (event.type)
155 			{
156 				case ENET_EVENT_TYPE_NONE:
157 					break;
158 				case ENET_EVENT_TYPE_CONNECT:
159 					isConnecting = false;
160 					isConnected = true;
161 					onConnect(event);
162 					break;
163 				case ENET_EVENT_TYPE_RECEIVE:
164 					onPacketReceived(event);
165 					break;
166 				case ENET_EVENT_TYPE_DISCONNECT:
167 					onDisconnect(event);
168 					isConnecting = false;
169 					isConnected = false;
170 					break;
171 			}
172 		}
173 	}
174 }