void hook_doAll(const char* hookID, ...) { va_list argList; va_start(argList, hookID); runAllCallback(hookID,argList); va_end(argList); }
void Logger::log(LogType::LogType type, const std::string& source, const std::string& message) { if (!ServerInstance->plugin()->hasHook("LogPost")) { std::clog.tie(&std::cout); if (type < LogType::LOG_WARNING) { std::clog.tie(&std::cerr); } std::clog << source << ": " << message << std::endl; return; } runAllCallback("LogPost",(int)type, source.c_str(), message.c_str()); }
int PacketHandler::player_digging(User* user) { int8_t status; int32_t x; int16_t y; int8_t temp_y; int32_t z; int8_t direction; uint8_t block; uint8_t meta; BlockBasicPtr blockcb; BlockDefault blockD; user->buffer >> status >> x >> temp_y >> z >> direction; y = (uint8_t)temp_y; if (!user->buffer) { return PACKET_NEED_MORE_DATA; } user->buffer.removePacket(); if (!ServerInstance->map(user->pos.map)->getBlock(x, y, z, &block, &meta)) { blockD.revertBlock(user, x, y, z, user->pos.map); return PACKET_OK; } // Blocks that break with first hit if(status == BLOCK_STATUS_STARTED_DIGGING) { if( user->creative || (block == BLOCK_SNOW || block == BLOCK_REED || block == BLOCK_TORCH || block == BLOCK_REDSTONE_WIRE || block == BLOCK_RED_ROSE || block == BLOCK_YELLOW_FLOWER || block == BLOCK_BROWN_MUSHROOM || block == BLOCK_RED_MUSHROOM || block == BLOCK_REDSTONE_TORCH_OFF || block == BLOCK_REDSTONE_TORCH_ON)) { status = BLOCK_STATUS_BLOCK_BROKEN; } } switch (status) { case BLOCK_STATUS_STARTED_DIGGING: { runAllCallback("PlayerDiggingStarted",user->nick.c_str(), x, y, z, direction); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && blockcb->affectedBlock(block)) { blockcb->onStartedDigging(user, status, x, y, z, user->pos.map, direction); } } break; } case BLOCK_STATUS_BLOCK_BROKEN: { //Player tool usage calculation etc bool rightUse; int16_t itemSlot = user->currentItemSlot() + 36; int16_t itemHealth = ServerInstance->inventory()->itemHealth(user->inv[itemSlot].getType(), block, rightUse); if (itemHealth > 0) { user->inv[itemSlot].incHealth(); if (!rightUse) { user->inv[itemSlot].incHealth(); } if (itemHealth <= user->inv[itemSlot].getHealth()) { user->inv[itemSlot].decCount(); if (user->inv[itemSlot].getCount() == 0) { user->inv[itemSlot].setHealth(0); user->inv[itemSlot].setType(-1); } } ServerInstance->inventory()->setSlot(user, WINDOW_PLAYER, itemSlot, user->inv[itemSlot].getType(), user->inv[itemSlot].getCount(), user->inv[itemSlot].getHealth()); } runCallbackUntilFalse("BlockBreakPre",user->nick.c_str(), x, y, z); if (callbackReturnValue) { blockD.revertBlock(user, x, y, z, user->pos.map); return PACKET_OK; } runAllCallback("BlockBreakPost",user->nick.c_str(), x, y, z); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && blockcb->affectedBlock(block)) { if (blockcb->onBroken(user, status, x, y, z, user->pos.map, direction)) { // Do not break return PACKET_OK; } else { break; } } } /* notify neighbour blocks of the broken block */ status = block; if (ServerInstance->map(user->pos.map)->getBlock(x + 1, y, z, &block, &meta) && block != BLOCK_AIR) { runAllCallback("BlockNeighbourBreak",user->nick.c_str(), x + 1, y, z, x, int8_t(y), z); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && (blockcb->affectedBlock(status) || blockcb->affectedBlock(block))) { blockcb->onNeighbourBroken(user, status, x + 1, y, z, user->pos.map, BLOCK_SOUTH); } } } if (ServerInstance->map(user->pos.map)->getBlock(x - 1, y, z, &block, &meta) && block != BLOCK_AIR) { runAllCallback("BlockNeighbourBreak",user->nick.c_str(), x - 1, y, z, x, int8_t(y), z); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && (blockcb->affectedBlock(status) || blockcb->affectedBlock(block))) { blockcb->onNeighbourBroken(user, status, x - 1, y, z, user->pos.map, BLOCK_NORTH); } } } if (ServerInstance->map(user->pos.map)->getBlock(x, y + 1, z, &block, &meta) && block != BLOCK_AIR) { runAllCallback("BlockNeighbourBreak",user->nick.c_str(), x, y + 1, z, x, int8_t(y), z); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && (blockcb->affectedBlock(status) || blockcb->affectedBlock(block))) { blockcb->onNeighbourBroken(user, status, x, y + 1, z, user->pos.map, BLOCK_TOP); } } } if (ServerInstance->map(user->pos.map)->getBlock(x, y - 1, z, &block, &meta) && block != BLOCK_AIR) { runAllCallback("BlockNeighbourBreak",user->nick.c_str(), x, y - 1, z, x, int8_t(y), z); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && (blockcb->affectedBlock(status) || blockcb->affectedBlock(block))) { blockcb->onNeighbourBroken(user, status, x, y - 1, z, user->pos.map, BLOCK_BOTTOM); } } } if (ServerInstance->map(user->pos.map)->getBlock(x, y, z + 1, &block, &meta) && block != BLOCK_AIR) { runAllCallback("BlockNeighbourBreak",user->nick.c_str(), x, y, z + 1, x, int8_t(y), z); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && (blockcb->affectedBlock(status) || blockcb->affectedBlock(block))) { blockcb->onNeighbourBroken(user, status, x, y, z + 1, user->pos.map, BLOCK_WEST); } } } if (ServerInstance->map(user->pos.map)->getBlock(x, y, z - 1, &block, &meta) && block != BLOCK_AIR) { runAllCallback("BlockNeighbourBreak",user->nick.c_str(), x, y, z - 1, x, int8_t(y), z); for (uint32_t i = 0 ; i < ServerInstance->plugin()->getBlockCB().size(); i++) { blockcb = ServerInstance->plugin()->getBlockCB()[i]; if (blockcb != NULL && (blockcb->affectedBlock(status) || blockcb->affectedBlock(block))) { blockcb->onNeighbourBroken(user, status, x, y, z - 1, user->pos.map, BLOCK_EAST); } } } break; } case BLOCK_STATUS_PICKUP_SPAWN: { //ToDo: handle #define itemSlot (36+user->currentItemSlot()) if (user->inv[itemSlot].getType() > 0) { ServerInstance->map(user->pos.map)->createPickupSpawn(int(user->pos.x), int(user->pos.y), int(user->pos.z), int(user->inv[itemSlot].getType()), 1, int(user->inv[itemSlot].getHealth()), user); user->inv[itemSlot].decCount(); } break; #undef itemSlot } } return PACKET_OK; }
int PacketHandler::handshake(User* user) { if (!user->buffer.haveData(9)) { return PACKET_NEED_MORE_DATA; } std::string player, host; int8_t version; int32_t port; user->buffer >> version >> player >> host >> port; // Check for data if (!user->buffer) { return PACKET_NEED_MORE_DATA; } // Remove package from buffer user->buffer.removePacket(); LOG(INFO, "Packets", "Player " + dtos(user->UID) + " login v." + dtos(version) + " : " + player); user->nick = player; // If version is not the current version if (version != PROTOCOL_VERSION) { user->kick(ServerInstance->config()->sData("strings.wrong_protocol")); return PACKET_OK; } // If userlimit is reached if ((int)User::all().size() > ServerInstance->config()->iData("system.user_limit")) { user->kick(ServerInstance->config()->sData("strings.server_full")); return PACKET_OK; } char* kickMessage = NULL; runCallbackUntilFalse("PlayerLoginPre",player.c_str(), &kickMessage); if (callbackReturnValue) { user->kick(std::string(kickMessage)); } else { //We can skip the protocol encryption if(!ServerInstance->config()->bData("system.protocol_encryption")) { user->sendLoginInfo(); } else { user->buffer << Protocol::encryptionRequest(); } runAllCallback("PlayerLoginPost",player.c_str()); } // TODO: Add support for prompting user for Server password return PACKET_OK; }
bool Mineserver::run() { uint32_t starttime = (uint32_t)time(0); uint32_t tick = (uint32_t)time(0); // load plugins if (config()->has("system.plugins") && (config()->type("system.plugins") == CONFIG_NODE_LIST)) { std::list<std::string> tmp = config()->mData("system.plugins")->keys(); for (std::list<std::string>::const_iterator it = tmp.begin(); it != tmp.end(); ++it) { std::string path = config()->sData("system.path.plugins"); std::string name = config()->sData("system.plugins." + (*it)); std::string alias = *it; if (name[0] == '_') { path = ""; alias = name; name = name.substr(1); } plugin()->loadPlugin(name, path, alias); } } // Initialize map for (int i = 0; i < (int)m_map.size(); i++) { physics(i)->enabled = (config()->bData("system.physics.enabled")); redstone(i)->enabled = (config()->bData("system.redstone.enabled")); m_map[i]->init(i); if (config()->bData("map.generate_spawn.enabled")) { LOG2(INFO, "Generating spawn area..."); int size = config()->iData("map.generate_spawn.size"); bool show_progress = config()->bData("map.generate_spawn.show_progress"); #ifdef __FreeBSD__ show_progress = false; #endif #ifdef WIN32 DWORD t_begin = 0, t_end = 0; #else clock_t t_begin = 0, t_end = 0; #endif for (int x = -size; x <= size; x++) { if (show_progress) { #ifdef WIN32 t_begin = timeGetTime(); #else t_begin = clock(); #endif } for (int z = -size; z <= size; z++) { m_map[i]->loadMap(x, z); } if (show_progress) { #ifdef WIN32 t_end = timeGetTime(); LOG2(INFO, dtos((x + size + 1) *(size * 2 + 1)) + "/" + dtos((size * 2 + 1) *(size * 2 + 1)) + " done. " + dtos((t_end - t_begin) / (size * 2 + 1)) + "ms per chunk"); #else t_end = clock(); LOG2(INFO, dtos((x + size + 1) *(size * 2 + 1)) + "/" + dtos((size * 2 + 1) *(size * 2 + 1)) + " done. " + dtos(((t_end - t_begin) / (CLOCKS_PER_SEC / 1000)) / (size * 2 + 1)) + "ms per chunk"); #endif } } } // Choose proper spawn position m_map[i]->chooseSpawnPosition(); #ifdef DEBUG LOG(DEBUG, "Map", "Spawn area ready!"); #endif } // Initialize packethandler packetHandler()->init(); // Load ip from config const std::string ip = config()->sData("net.ip"); // Load port from config const int port = config()->iData("net.port"); #ifdef WIN32 WSADATA wsaData; int iResult; // Initialize Winsock iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { LOG2(ERROR, std::string("WSAStartup failed with error: " + iResult)); return false; } #endif struct sockaddr_in addresslisten; int reuse = 1; m_eventBase = reinterpret_cast<event_base*>(event_init()); #ifdef WIN32 m_socketlisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); #else m_socketlisten = socket(AF_INET, SOCK_STREAM, 0); #endif if (m_socketlisten < 0) { LOG2(ERROR, "Failed to create listen socket"); return false; } memset(&addresslisten, 0, sizeof(addresslisten)); addresslisten.sin_family = AF_INET; addresslisten.sin_addr.s_addr = inet_addr(ip.c_str()); addresslisten.sin_port = htons(port); //Reuse the socket setsockopt(m_socketlisten, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)); // Bind to port if (bind(m_socketlisten, (struct sockaddr*)&addresslisten, sizeof(addresslisten)) < 0) { LOG2(ERROR, "Failed to bind to " + ip + ":" + dtos(port)); return false; } if (listen(m_socketlisten, 5) < 0) { LOG2(ERROR, "Failed to listen to socket"); return false; } setnonblock(m_socketlisten); event_set(&m_listenEvent, m_socketlisten, EV_WRITE | EV_READ | EV_PERSIST, accept_callback, NULL); event_add(&m_listenEvent, NULL); LOG2(INFO, "Listening on: "); if (ip == "0.0.0.0") { // Print all local IPs char name[255]; gethostname(name, sizeof(name)); struct hostent* hostinfo = gethostbyname(name); int ipIndex = 0; while (hostinfo && hostinfo->h_addr_list[ipIndex]) { const std::string ip(inet_ntoa(*(struct in_addr*)hostinfo->h_addr_list[ipIndex++])); LOG2(INFO, ip + ":" + dtos(port)); } } else { LOG2(INFO, ip + ":" + dtos(port)); } //Let event_base_loop lock for 200ms timeval loopTime; loopTime.tv_sec = 0; loopTime.tv_usec = 200000; // 200ms m_running = true; event_base_loopexit(m_eventBase, &loopTime); // Create our Server Console user so we can issue commands time_t timeNow = time(NULL); while (m_running && event_base_loop(m_eventBase, 0) == 0) { event_base_loopexit(m_eventBase, &loopTime); // Run 200ms timer hook runAllCallback("Timer200"); //Remove any users pending removal if(m_usersToRemove.size()) { for (std::set<User*>::iterator it = m_usersToRemove.begin(); it != m_usersToRemove.end(); it++) { User* u = *it; delete u; u = 0; } m_usersToRemove.clear(); } // Alert any block types that care about timers for (size_t i = 0 ; i < plugin()->getBlockCB().size(); ++i) { const BlockBasicPtr blockcb = plugin()->getBlockCB()[i]; if (blockcb != NULL) { blockcb->timer200(); } } //Update physics every 200ms for (std::vector<Map*>::size_type i = 0 ; i < m_map.size(); i++) { physics(i)->update(); redstone(i)->update(); } //Every 10 seconds.. timeNow = time(0); if (timeNow - starttime > 10) { starttime = (uint32_t)timeNow; //Map saving on configurable interval if (m_saveInterval != 0 && timeNow - m_lastSave >= m_saveInterval) { //Save for (std::vector<Map*>::size_type i = 0; i < m_map.size(); i++) { m_map[i]->saveWholeMap(); } m_lastSave = timeNow; } // If users, ping them if (!User::all().empty()) { // Send server time and keepalive Packet pkt; pkt << Protocol::timeUpdate(m_map[0]->mapTime); pkt << Protocol::keepalive(0); pkt << Protocol::playerlist(); (*User::all().begin())->sendAll(pkt); } //Check for tree generation from saplings for (size_t i = 0; i < m_map.size(); ++i) { m_map[i]->checkGenTrees(); } // TODO: Run garbage collection for chunk storage dealie? // Run 10s timer hook runAllCallback("Timer10000"); } // Every second if (timeNow - tick > 0) { tick = (uint32_t)timeNow; std::set<User*> usersToRemove; // Loop users for (std::set<User*>::iterator it = m_users.begin(); it != m_users.end(); it++) { User * const & u = *it; // No data received in 30s, timeout if (u->logged && timeNow - u->lastData > 30) { LOG2(INFO, "Player " + u->nick + " timed out"); usersToRemove.insert(u); } else if (!u->logged && timeNow - u->lastData > 100) { usersToRemove.insert(u); } else { if (m_damage_enabled) { u->checkEnvironmentDamage(); } u->pushMap(); u->popMap(); } } for (std::set<User*>::iterator it = usersToRemove.begin(); it != usersToRemove.end(); it++) { delete *it; } for (std::vector<Map*>::size_type i = 0 ; i < m_map.size(); i++) { m_map[i]->mapTime += 20; if (m_map[i]->mapTime >= 24000) { m_map[i]->mapTime = 0; } } for (std::set<User*>::const_iterator it = users().begin(); it != users().end(); ++it) { (*it)->pushMap(); (*it)->popMap(); } // Check for Furnace activity furnaceManager()->update(); // Check for user validation results pthread_mutex_lock(&ServerInstance->m_validation_mutex); for(size_t i = 0; i < ServerInstance->validatedUsers.size(); i++) { //To make sure user hasn't timed out or anything while validating User *tempuser = NULL; for (std::set<User*>::const_iterator it = users().begin(); it != users().end(); ++it) { if((*it)->UID == ServerInstance->validatedUsers[i].UID) { tempuser = (*it); break; } } if(tempuser != NULL) { if(ServerInstance->validatedUsers[i].valid) { LOG(INFO, "Packets", tempuser->nick + " is VALID "); tempuser->crypted = true; tempuser->buffer << (int8_t)PACKET_ENCRYPTION_RESPONSE << (int16_t)0 << (int16_t) 0; tempuser->uncryptedLeft = 5; } else { tempuser->kick("User not Premium"); } //Flush client_write(tempuser); } } ServerInstance->validatedUsers.clear(); pthread_mutex_unlock(&ServerInstance->m_validation_mutex); // Run 1s timer hook runAllCallback("Timer1000"); } // Underwater check / drowning // ToDo: this could be done a bit differently? - Fador // -- User::all() == users() - louisdx for (std::set<User*>::const_iterator it = users().begin(); it != users().end(); ++it) { (*it)->isUnderwater(); if ((*it)->pos.y < 0) { (*it)->sethealth((*it)->health - 5); } //Flush data client_write((*it)); } } #ifdef WIN32 closesocket(m_socketlisten); #else close(m_socketlisten); #endif saveAll(); event_base_free(m_eventBase); return true; }