1 /* 2 * Copyright (c) 2017-2018 SEL 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 12 * See the GNU Lesser General Public License for more details. 13 * 14 */ 15 /** 16 * Copyright: 2017-2020 sel-project 17 * License: LGPL-3.0 18 * Authors: Kripth 19 * Source: $(HTTP github.com/sel-project/sel-server/sel/server/server.d, sel/server/server.d) 20 */ 21 module sel.server.server; 22 23 import core.atomic : atomicOp; 24 25 import std.algorithm : sort, all, canFind; 26 import std.conv : to; 27 import std.socket : Address, getAddress; 28 import std.typetuple : TypeTuple; 29 30 import sel.server.client : Client; 31 import sel.server.query : Query; 32 33 /** 34 * Basic server's informations, used to display the server in the 35 * game's server list and in the queries. 36 */ 37 class ServerInfo { 38 39 static struct MOTD { 40 41 string raw; 42 43 string bedrock, java; 44 45 this(string motd) { 46 this.opAssign(motd); 47 } 48 49 void opAssign(string motd) { 50 this.raw = motd; //TODO remove formatting 51 this.bedrock = this.java = motd; 52 } 53 54 shared void opAssign(string motd) { 55 (cast()this).opAssign(motd); 56 } 57 58 } 59 60 public MOTD motd = MOTD("A Minecraft Server"); 61 62 public int online = 0; 63 public int max = 32; 64 65 public string favicon; // must be already encoded 66 67 public string gametype = "SMP"; 68 public string map = "world"; 69 70 } 71 72 abstract class GenericServer { 73 74 protected shared ServerInfo info; 75 76 public shared this(shared ServerInfo info) { 77 this.info = info; 78 } 79 80 /** 81 * Starts the server on the given address. 82 * The server can be started using an ip/port combination (if the port 83 * is not given the game's default one will be used), and an optional 84 * handler (for the management of the players) and a query. 85 */ 86 public final shared void start(Address address, shared Query query=null) { 87 this.startImpl(address, query); 88 } 89 90 /// ditto 91 public final shared void start(string ip, ushort port, shared Query query=null) { 92 this.start(getAddress(ip, port)[0], query); 93 } 94 95 /// ditto 96 public final shared void start(string ip, shared Query query=null) { 97 this.start(ip, this.defaultPort, query); 98 } 99 100 protected abstract shared void startImpl(Address address, shared Query query); 101 102 /** 103 * Gets the server's default port for the hosted game. 104 */ 105 public abstract shared pure nothrow @property @safe @nogc ushort defaultPort(); 106 107 } 108 109 /** 110 * A generic server that only contains a ServerInfo, the supported protocols 111 * and the mothods to start it. 112 */ 113 abstract class GenericGameServer : GenericServer { 114 115 private immutable immutable(uint)[] supported; 116 protected immutable(uint)[] _protocols; 117 protected shared Handler handler; 118 119 public shared this(shared ServerInfo info, uint[] protocols, uint[] supported, shared Handler handler) { 120 super(info); 121 this.supported = supported.idup; 122 this.protocols = protocols; 123 this.handler = handler; 124 } 125 126 public final shared pure nothrow @property @safe @nogc immutable(uint)[] protocols() { 127 return this._protocols; 128 } 129 130 public final shared @property immutable(uint)[] protocols(uint[] protocols) { 131 return this._protocols = checkProtocols(protocols, this.supported).idup; 132 } 133 134 protected shared void onClientJoin(shared Client client) { 135 atomicOp!"+="(this.info.online, 1); 136 this.handler.onClientJoin(client); 137 } 138 139 protected shared void onClientLeft(shared Client client) { 140 atomicOp!"-="(this.info.online, 1); 141 this.handler.onClientLeft(client); 142 } 143 144 } 145 146 class Handler { 147 148 public shared void onClientJoin(shared Client client) {} 149 150 public shared void onClientLeft(shared Client client) {} 151 152 public shared void onClientPacket(shared Client client, ubyte[] packet) {} 153 154 } 155 156 uint[] checkProtocols(uint[] protocols, inout(uint)[] supported) { 157 sort(protocols); 158 uint[] ret; 159 foreach(i, protocol; protocols) { 160 if(supported.canFind(protocol) && (ret.length == 0 || protocol != ret[$-1])) { 161 ret ~= protocol; 162 } 163 } 164 return ret; 165 } 166 167 template TupleOf(alias array) { 168 mixin((){ 169 string ret = "alias TupleOf = TypeTuple!("; 170 foreach(element ; array) { 171 ret ~= element.to!string; 172 ret ~= ","; 173 } 174 return ret ~ ");"; 175 }()); 176 }