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 }