int Recvmsg(int s, struct msghdr *msgh, int flags) { int retval, _errno; char infobuff[256]; #if defined(HAVE_STRUCT_MSGHDR_MSGCONTROL) && defined(HAVE_STRUCT_MSGHDR_MSGCONTROLLEN) && defined(HAVE_STRUCT_MSGHDR_MSGFLAGS) Debug10("recvmsg(%d, %p{%p,%u,%p,%u,%p,%u,%d}, %d)", s, msgh, msgh->msg_name, msgh->msg_namelen, msgh->msg_iov, msgh->msg_iovlen, msgh->msg_control, msgh->msg_controllen, msgh->msg_flags, flags); #else Debug7("recvmsg(%d, %p{%p,%u,%p,%u}, %d)", s, msgh, msgh->msg_name, msgh->msg_namelen, msgh->msg_iov, msgh->msg_iovlen, flags); #endif retval = recvmsg(s, msgh, flags); _errno = errno; #if defined(HAVE_STRUCT_MSGHDR_MSGCONTROLLEN) Debug5("recvmsg(, {%s,%u,,%u,,%u,}, ) -> %d", msgh->msg_name?sockaddr_info(msgh->msg_name, msgh->msg_namelen, infobuff, sizeof(infobuff)):"NULL", msgh->msg_namelen, msgh->msg_iovlen, msgh->msg_controllen, retval); #else Debug4("recvmsg(, {%s,%u,,%u,,}, ) -> %d", msgh->msg_name?sockaddr_info(msgh->msg_name, msgh->msg_namelen, infobuff, sizeof(infobuff)):"NULL", msgh->msg_namelen, msgh->msg_iovlen, retval); #endif errno = _errno; return retval; }
int Gettimeofday(struct timeval *tv, struct timezone *tz) { int result, _errno; #if WITH_MSGLEVEL <= E_DEBUG if (tz) { Debug3("gettimeofday(%p, {%d,%d})", tv, tz->tz_minuteswest, tz->tz_dsttime); } else { Debug1("gettimeofday(%p, NULL)", tv); } #endif /* WITH_MSGLEVEL <= E_DEBUG */ result = gettimeofday(tv, tz); _errno = errno; #if WITH_MSGLEVEL <= E_DEBUG if (tz) { Debug5("gettimeofday({%ld,%ld}, {%d,%d}) -> %d", tv->tv_sec, tv->tv_usec, tz->tz_minuteswest, tz->tz_dsttime, result); } else { Debug3("gettimeofday({%ld,%ld},) -> %d", tv->tv_sec, tv->tv_usec, result); } #endif /* WITH_MSGLEVEL <= E_DEBUG */ errno = _errno; return result; }
int Send(int s, const void *mesg, size_t len, int flags) { int retval, _errno; Debug5("send(%d, %p[%08x...], "F_Zu", %d)", s, mesg, ntohl(*(unsigned long *)mesg), len, flags); retval = send(s, mesg, len, flags); _errno = errno; Debug1("send() -> %d", retval); errno = _errno; return retval; }
int Getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen) { int result, _errno; Debug5("getsockopt(%d, %d, %d, %p, {"F_Zd"})", s, level, optname, optval, *optlen); result = getsockopt(s, level, optname, optval, optlen); _errno = errno; Debug3("getsockopt() -> (,,, 0x%08x, %d), %d", *(int *)optval, *optlen, result); errno = _errno; return result; }
int Openpty(int *ptyfd, int *ttyfd, char *ptyname, struct termios *termp, struct winsize *winp) { int result, _errno; Debug5("openpty(%p, %p, %p, %p, %p)", ptyfd, ttyfd, ptyname, termp, winp); result = openpty(ptyfd, ttyfd, ptyname, termp, winp); _errno = errno; Info4("openpty({%d}, {%d}, {\"%s\"},,) -> %d", *ptyfd, *ttyfd, ptyname, result); errno = _errno; return result; }
int Uname(struct utsname *buf) { int result, _errno; Debug1("uname(%p)", buf); result = uname(buf); _errno = errno; #if UNAME_DOMAINNAME Debug6("uname({%s, %s, %s, %s, %s, %s})", buf->sysname, buf->nodename, buf->release, buf->version, buf->machine, buf->domainname); #else Debug5("uname({%s, %s, %s, %s, %s})", buf->sysname, buf->nodename, buf->release, buf->version, buf->machine); #endif errno = _errno; return result; }
int Setsockopt(int s, int level, int optname, const void *optval, int optlen) { int result, _errno; if (optlen <= sizeof(int)) { Debug5("setsockopt(%d, %d, %d, {0x%x}, %d)", s, level, optname, *(unsigned int *)optval, optlen); } else { Debug6("setsockopt(%d, %d, %d, {0x%08x,%08x}, %d)", s, level, optname, ((unsigned int *)optval)[0], ((unsigned int *)optval)[1], optlen); } result = setsockopt(s, level, optname, optval, optlen); _errno = errno; Debug1("setsockopt() -> %d", result); errno = _errno; return result; }
/* we only show the first struct pollfd; hope this is enough for most cases. */ int Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { int result; if (nfds == 4) { Debug10("poll({%d,0x%02hx,}{%d,0x%02hx,}{%d,0x%02hx,}{%d,0x%02hx,}, %u, %d)", ufds[0].fd, ufds[0].events, ufds[1].fd, ufds[1].events, ufds[2].fd, ufds[2].events, ufds[3].fd, ufds[3].events, nfds, timeout); } else { Debug4("poll({%d,0x%02hx,}, , %u, %d)", ufds[0].fd, ufds[0].events, nfds, timeout); } result = poll(ufds, nfds, timeout); if (nfds == 4) { Debug5("poll(, {,,0x%02hx}{,,0x%02hx}{,,0x%02hx}{,,0x%02hx}) -> %d", ufds[0].revents, ufds[1].revents, ufds[2].revents, ufds[3].revents, result); } else { Debug2("poll(, {,,0x%02hx}) -> %d", ufds[0].revents, result); } return result; }
int Execvp(const char *file, char *const argv[]) { int result, _errno; if (argv[1] == NULL) Debug2("execvp(\"%s\", \"%s\")", file, argv[0]); else if (argv[2] == NULL) Debug3("execvp(\"%s\", \"%s\" \"%s\")", file, argv[0], argv[1]); else if (argv[3] == NULL) Debug4("execvp(\"%s\", \"%s\" \"%s\" \"%s\")", file, argv[0], argv[1], argv[2]); else if (argv[4] == NULL) Debug5("execvp(\"%s\", \"%s\" \"%s\" \"%s\" \"%s\")", file, argv[0], argv[1], argv[2], argv[3]); else if (argv[5] == NULL) Debug6("execvp(\"%s\", \"%s\" \"%s\" \"%s\" \"%s\" \"%s\")", file, argv[0], argv[1], argv[2], argv[3], argv[4]); else Debug6("execvp(\"%s\", \"%s\" \"%s\" \"%s\" \"%s\" \"%s\" ...)", file, argv[0], argv[1], argv[2], argv[3], argv[4]); result = execvp(file, argv); _errno = errno; Debug1("execvp() -> %d", result); errno = _errno; return result; }
bool PaladinJr::SpeedCheck(Client* client, gemActor* actor, psDRMessage& currUpdate) { csVector3 oldpos; // Dummy variables float yrot; iSector* sector; psWorld * world = entitymanager->GetWorld(); int violation = NOVIOLATION; actor->pcmove->GetLastClientPosition (oldpos, yrot, sector); // If no previous observations then we have nothing to check against. if (!sector) return true; // define cheating variables float dist; float reported_distance; float max_noncheat_distance; float lag_distance; csTicks timedelta; csVector3 vel; // check for warpviolation if (sector != currUpdate.sector && !world->WarpSpace(sector, currUpdate.sector, oldpos)) { if (checks & WARPVIOLATION) { violation = WARPVIOLATION; } else { // we don't do warp checking and crossed a sector // skip this round return true; } } if (checks & SPEEDVIOLATION) { // we don't use the absolute value of the vertical // speed in order to let falls go through if (fabs(currUpdate.vel.x) <= maxVelocity.x && currUpdate.vel.y <= maxVelocity.y && fabs(currUpdate.vel.z) <= maxVelocity.z) { violation |= SPEEDVIOLATION; } } // distance check is skipped on warp violation as it would be wrong if (checks & DISTVIOLATION && !(violation & WARPVIOLATION)) { dist = (currUpdate.pos-oldpos).Norm(); timedelta = actor->pcmove->ClientTimeDiff(); // We use the last reported vel, not the new vel, to calculate how far he should have gone since the last DR update vel = actor->pcmove->GetVelocity(); vel.y = 0; // ignore vertical velocity reported_distance = vel.Norm()*timedelta/1000; Debug4(LOG_CHEAT, client->GetClientNum(),"Player went %1.3fm in %u ticks when %1.3fm was allowed.\n",dist, timedelta, reported_distance); max_noncheat_distance = maxSpeed*timedelta/1000; lag_distance = maxSpeed*client->accumulatedLag/1000; if (dist < max_noncheat_distance + lag_distance) { if(dist == 0) { // player is stationary - reset accumulated lag NetBase::Connection * connection = client->GetConnection(); client->accumulatedLag = connection->estRTT + connection->devRTT; } else if(fabs(dist-reported_distance) < dist/20) { // ignore jitter caused differences Debug1(LOG_CHEAT, client->GetClientNum(),"Ignoring lag jitter."); } else { // adjust accumulated lag float lag = (reported_distance - dist) * 1000.f/maxSpeed + client->accumulatedLag; // cap to meaningful values lag = lag < 0 ? 0 : lag > MAX_ACCUMULATED_LAG ? MAX_ACCUMULATED_LAG : lag; client->accumulatedLag = (csTicks)lag; Debug2(LOG_CHEAT, client->GetClientNum(),"Accumulated lag: %u\n",client->accumulatedLag); } } else { violation |= DISTVIOLATION; } } if (violation != NOVIOLATION) { if (client->GetCheatMask(MOVE_CHEAT)) { //printf("Server has pre-authorized this apparent speed violation.\n"); client->SetCheatMask(MOVE_CHEAT, false); // now clear the Get Out of Jail Free card return true; // not cheating } Debug6(LOG_CHEAT, client->GetClientNum(),"Went %1.2f in %u ticks when %1.2f was expected plus %1.2f allowed lag distance (%1.2f)\n", dist, timedelta, max_noncheat_distance, lag_distance, max_noncheat_distance+lag_distance); //printf("Z Vel is %1.2f\n", currUpdate.vel.z); //printf("MaxSpeed is %1.2f\n", maxSpeed); // Report cheater csVector3 angVel; csString buf; csString type; csString sectorName(sector->QueryObject()->GetName()); // Player has probably been warped if (violation & WARPVIOLATION) { sectorName.Append(" to "); sectorName.Append(currUpdate.sectorName); type = "Warp Violation"; } if (violation & SPEEDVIOLATION) { if(!type.IsEmpty()) type += "|"; type += "Speed Violation (Hack confirmed)"; } if (violation & DISTVIOLATION) { if(!type.IsEmpty()) type += "|"; type += "Distance Violation"; } if (enforcing) { actor->ForcePositionUpdate(); } actor->pcmove->GetAngularVelocity(angVel); buf.Format("%s, %s, %s, %.3f %.3f %.3f, %.3f 0 %.3f, %.3f %.3f %.3f, %.3f %.3f %.3f, %.3f %.3f %.3f, %s\n", client->GetName(), type.GetData(), sectorName.GetData(),oldpos.x, oldpos.y, oldpos.z, max_noncheat_distance, max_noncheat_distance, currUpdate.pos.x - oldpos.x, currUpdate.pos.y - oldpos.y, currUpdate.pos.z - oldpos.z, vel.x, vel.y, vel.z, angVel.x, angVel.y, angVel.z, PALADIN_VERSION); psserver->GetLogCSV()->Write(CSV_PALADIN, buf); Debug5(LOG_CHEAT, client->GetClientNum(),"Player %s traversed %1.2fm in %u msec with an accumulated lag allowance of %u ms. Cheat detected!\n", client->GetName (),dist,timedelta,client->accumulatedLag); client->CountDetectedCheat(); //printf("Client has %d detected cheats now.\n", client->GetDetectedCheatCount()); if (client->GetDetectedCheatCount() % warnCount == 0) { psserver->SendSystemError(client->GetClientNum(),"You have been flagged as using speed hacks. You will be disconnected if you continue."); } if (client->GetDetectedCheatCount() >= maxCount) { //printf("Disconnecting a cheating client.\n"); psserver->RemovePlayer(client->GetClientNum(),"Paladin has kicked you from the server for cheating."); return false; } return !enforcing; } else { return true; } }
bool NetBase::CheckIn() { // check for incoming packets SOCKADDR_IN addr; memset (&addr, 0, sizeof(SOCKADDR_IN)); socklen_t len = sizeof(SOCKADDR_IN); if (!input_buffer) { input_buffer = (char*) cs_malloc(MAXPACKETSIZE); if (!input_buffer) { Error2("Failed to cs_malloc %d bytes for packet buffer!\n",MAXPACKETSIZE); return false; } } // Connection must be initialized! CS_ASSERT(ready); int packetlen = RecvFrom (&addr, &len, (void*) input_buffer, MAXPACKETSIZE); if (packetlen <= 0) { return false; } // Identify the connection Connection* connection = GetConnByIP(&addr); // Extract the netpacket from the buffer and prep for use locally. psNetPacket *bufpacket = psNetPacket::NetPacketFromBuffer(input_buffer,packetlen); if (bufpacket==NULL) { char addrText[INET_ADDRSTRLEN]; //for win32 for now only inet_ntoa as inet_ntop wasn't supported till vista. //it has the same degree of compatibility of the previous code and it's supported till win2000 #ifdef WIN32 strncpy(addrText, inet_ntoa(addr.sin_addr), INET_ADDRSTRLEN); #else //there was a failure in conversion if null if(!inet_ntop(addr.sin_family,&addr.sin_addr, addrText, sizeof(addrText))) { strncpy(addrText, "UNKNOWN", INET_ADDRSTRLEN); } #endif // The data received was too small to make a full packet. if (connection) { Debug4(LOG_NET, connection->clientnum, "Too short packet received from client %d (IP: %s) (%d bytes)", connection->clientnum, addrText, packetlen); } else { Debug3(LOG_NET, 0, "Too short packet received from IP address %s. (%d bytes) No existing connection from this IP.", addrText, packetlen); } return true; // Continue processing more packets if available } input_buffer = NULL; //input_buffer now hold by the bufpacket pointer. // Endian correction bufpacket->UnmarshallEndian(); // Check for too-big packets - no harm in processing them, but probably a bug somewhere if (bufpacket->GetPacketSize() < static_cast<unsigned int>(packetlen)) { char addrText[INET_ADDRSTRLEN]; //for win32 for now only inet_ntoa as inet_ntop wasn't supported till vista. //it has the same degree of compatibility of the previous code and it's supported till win2000 #ifdef WIN32 strncpy(addrText, inet_ntoa(addr.sin_addr), INET_ADDRSTRLEN); #else //there was a failure in conversion if null if(!inet_ntop(addr.sin_family,&addr.sin_addr, addrText, sizeof(addrText))) { strncpy(addrText, "UNKNOWN", INET_ADDRSTRLEN); } #endif if (connection) { Debug5(LOG_NET, connection->clientnum, "Too long packet received from client %d (IP: %s) (%d bytes received, header reports %zu bytes)", connection->clientnum, addrText, packetlen, bufpacket->GetPacketSize()); } else { Debug4(LOG_NET, 0,"Too long packet received from IP address %s. (%d bytes received, header reports %zu bytes) No existing connection from this IP.", addrText, packetlen, bufpacket->GetPacketSize()); } } //Create new net packet entry and transfer ownership of bufpacket to pkt. csRef<psNetPacketEntry> pkt; pkt.AttachNew(new psNetPacketEntry( bufpacket, connection ? connection->clientnum : 0, packetlen)); if(TEST_PACKETLOSS > 0.0 && randomgen->Get() < TEST_PACKETLOSS) { psNetPacket* packet = pkt->packet; int type = 0; if (packet->offset == 0) { psMessageBytes* msg = (psMessageBytes*) packet->data; type = msg->type; } Error3("Packet simulated lost. Type %s ID %d.\n", type == 0 ? "Fragment" : (const char *) GetMsgTypeName(type), pkt->packet->pktid); return true; } // ACK packets can get eaten by HandleAck if (HandleAck(pkt, connection, &addr)) { return true; } // printf("Got packet with sequence %d.\n", pkt->packet->GetSequence()); // // Check for doubled packets and drop them if (pkt->packet->pktid != 0) { if (connection && CheckDoublePackets (connection, pkt)) { #ifdef PACKETDEBUG Debug2(LOG_NET,0,"Dropping doubled packet (ID %d)\n", pkt->packet->pktid); #endif return true; } } #ifdef PACKETDEBUG Debug7(LOG_NET,0,"Received Pkt, ID: %d, offset %d, from %d size %d (actual %d) flags %d\n", pkt->packet->pktid, pkt->packet->offset, pkt->clientnum, pkt->packet->pktsize,packetlen, pkt->packet->flags); #endif /** * Now either send this packet to BuildMessage, or loop through * subpackets if they are merged. */ csRef<psNetPacketEntry> splitpacket = pkt; psNetPacket *packetdata = NULL; do { splitpacket = pkt->GetNextPacket(packetdata); if (splitpacket) BuildMessage(splitpacket, connection, &addr); } while (packetdata); return true; }
void ChatManager::HandleChatMessage(MsgEntry *me, Client *client) { psChatMessage msg(me); // Dont if (!msg.valid) { Debug2(LOG_NET,me->clientnum,"Received unparsable psChatMessage from client %u.\n",me->clientnum); return; } const char *pType = msg.GetTypeText(); if (msg.iChatType != CHAT_TELL && msg.iChatType != CHAT_AWAY) { Debug4(LOG_CHAT, client->GetClientNum(), "%s %s: %s\n", client->GetName(), pType, msg.sText.GetData()); } else { Debug5(LOG_CHAT,client->GetClientNum(), "%s %s %s: %s\n", client->GetName(), pType, msg.sPerson.GetData(),msg.sText.GetData()); } bool saveFlood = true; if (!client->IsMute()) { // Send Chat to other players switch (msg.iChatType) { case CHAT_GUILD: { SendGuild(client, msg); break; } case CHAT_ALLIANCE: { SendAlliance(client, msg); break; } case CHAT_GROUP: { SendGroup(client, msg); break; } case CHAT_AUCTION: case CHAT_SHOUT: { SendShout(client, msg); break; } case CHAT_CHANNEL: { csArray<uint32_t> subscribed = channelSubscriptions.GetAll(client->GetClientNum()); bool found = false; for(size_t i = 0; i < subscribed.GetSize(); i++) { if(subscribed[i] == msg.channelID) found = true; } if(!found) { psserver->SendSystemError(client->GetClientNum(), "You have not yet joined this channel."); break; } // channel 1 is public if(msg.channelID == 1) CPrintf (CON_WARNING, "Gossip %s: %s\n", client->GetName(), msg.sText.GetData()); psChatMessage newMsg(client->GetClientNum(), client->GetActor()->GetEID(), client->GetName(), 0, msg.sText, msg.iChatType, msg.translate, msg.channelID); csArray<uint32_t> subscribers = channelSubscribers.GetAll(msg.channelID); csArray<PublishDestination> destArray; for (size_t i = 0; i < subscribers.GetSize(); i++) { destArray.Push(PublishDestination(subscribers[i], NULL, 0, 0)); Client *target = psserver->GetConnections()->Find(subscribers[i]); if (target && target->IsReady()) target->GetActor()->LogChatMessage(client->GetActor()->GetFirstName(), newMsg); } newMsg.Multicast(destArray, 0, PROX_LIST_ANY_RANGE ); break; } case CHAT_PET_ACTION: { gemNPC *pet = NULL; // Check if a specific pet's name was specified, in one of these forms: // - /mypet Petname ... // - /mypet Petname's ... size_t numPets = client->GetNumPets(); for (size_t i = 0; i < numPets; i++) { if ((pet = dynamic_cast <gemNPC*>(client->GetPet(i))) && msg.sText.StartsWith(pet->GetCharacterData()->GetCharName(), true)) { size_t n = strlen(pet->GetCharacterData()->GetCharName()); if (msg.sText.Length() >= n + 1 && msg.sText.GetAt(n) == ' ') { msg.sText.DeleteAt(0, n); msg.sText.LTrim(); break; } else if (msg.sText.Length() >= n + 3 && msg.sText.GetAt(n) == '\'' && msg.sText.GetAt(n + 1) == 's' && msg.sText.GetAt(n + 2) == ' ') { msg.sText.DeleteAt(0, n); break; } } else pet = NULL; } // If no particular pet was specified, assume the default familiar... if (!pet) pet = dynamic_cast <gemNPC*>(client->GetFamiliar()); // Send the message or an appropriate error... if (!pet) psserver->SendSystemInfo(me->clientnum, "You have no familiar to command."); else SendSay(client->GetClientNum(), pet, msg, pet->GetCharacterData()->GetCharFullName()); break; } case CHAT_SAY: { // Send to all if there's no NPC response or the response is public SendSay(client->GetClientNum(), client->GetActor(), msg, client->GetName()); break; } case CHAT_NPC: { // Only the speaker sees his successful chatting with an npc. // This helps quests stay secret. //psChatMessage newMsg(client->GetClientNum(), client->GetName(), 0, // msg.sText, msg.iChatType, msg.translate); //newMsg.SendMessage(); saveFlood = false; gemObject *target = client->GetTargetObject(); gemNPC *targetnpc = dynamic_cast<gemNPC*>(target); NpcResponse *resp = CheckNPCResponse(msg,client,targetnpc); if (resp) { csTicks delay = resp->ExecuteScript(client->GetActor(), targetnpc); if (delay != (csTicks)-1 && resp->menu ) resp->menu->ShowMenu(client, delay, targetnpc); } break; } case CHAT_AWAY: { saveFlood = false; //do not check Away messages for flooding msg.iChatType = CHAT_TELL; //do regard it as tell message from now on //intentionally no break, so it falls through to CHAT_TELL } case CHAT_TELL: { if ( msg.sPerson.Length() == 0 ) { psserver->SendSystemError(client->GetClientNum(), "You must specify name of player."); break; } Client *target = FindPlayerClient(msg.sPerson); if (target && !target->IsSuperClient()) { if (!target->IsReady()) psserver->SendSystemError(client->GetClientNum(), "%s is not ready yet.", msg.sPerson.GetDataSafe()); else SendTell(msg, client->GetName(), client, target); } else psserver->SendSystemError(client->GetClientNum(), "%s is not found online.", msg.sPerson.GetDataSafe()); break; } case CHAT_REPORT: { // First thing to extract the name of the player to log csString targetName; int index = (int)msg.sText.FindFirst(' ', 0); targetName = (index == -1) ? msg.sText : msg.sText.Slice(0, index); targetName = NormalizeCharacterName(targetName); if ( targetName.Length() == 0 ) { psserver->SendSystemError(client->GetClientNum(), "You must specify name of player."); break; } Client * target = psserver->GetConnections()->Find(targetName); if ( !target ) { psserver->SendSystemError(client->GetClientNum(), "%s is not found online.", targetName.GetData()); break; } if (target->IsSuperClient()) { psserver->SendSystemError(client->GetClientNum(), "Can't report NPCs."); break; } // Add an active report to the target. if (target->GetActor()->AddChatReport(client->GetActor())) { // Add report removal event. psserver->GetEventManager()->Push(new psEndChatLoggingEvent(target->GetClientNum(), 300000)); psserver->SendSystemInfo(client->GetClientNum(), "Last 5 minutes of %s's chat were logged. Logging will continue for another 5 minutes.", targetName.GetData()); } else psserver->SendSystemError(client->GetClientNum(), "Could not start logging %s, due to a server error.", targetName.GetData()); break; } case CHAT_ADVISOR: case CHAT_ADVICE: break; default: { Error2("Unknown Chat Type: %d\n",msg.iChatType); break; } } } else { //User is muted but tries to chat anyway. Remind the user that he/she/it is muted psserver->SendSystemInfo(client->GetClientNum(),"You can't send messages because you are muted."); } if (saveFlood) client->FloodControl(msg.iChatType, msg.sText, msg.sPerson); }
void CombatManager::AttackSomeone(gemActor *attacker,gemObject *target,Stance stance) { psCharacter *attacker_character = attacker->GetCharacterData(); //we don't allow an overweight or defeated char to fight if (attacker->GetMode() == PSCHARACTER_MODE_DEFEATED || attacker->GetMode() == PSCHARACTER_MODE_OVERWEIGHT) return; if (attacker->GetMode() == PSCHARACTER_MODE_COMBAT) // Already fighting { SetCombat(attacker,stance); // switch stance from Bloody to Defensive, etc. return; } else { if (attacker->GetMode() == PSCHARACTER_MODE_SIT) //we are sitting force the char to stand attacker->Stand(); attacker_character->ResetSwings(csGetTicks()); } // Indicator of whether any weapons are available to attack with bool startedAttacking=false; bool haveWeapon=false; // Step through each current slot and queue events for all that can attack for (int slot=0; slot<PSCHARACTER_SLOT_BULK1; slot++) { // See if this slot is able to attack if (attacker_character->Inventory().CanItemAttack((INVENTORY_SLOT_NUMBER) slot)) { INVENTORY_SLOT_NUMBER weaponSlot = (INVENTORY_SLOT_NUMBER) slot; // Get the data for the "weapon" that is used in this slot psItem *weapon=attacker_character->Inventory().GetEffectiveWeaponInSlot(weaponSlot); csString response; if (weapon!=NULL && weapon->CheckRequirements(attacker_character,response) ) { haveWeapon = true; Debug5(LOG_COMBAT,attacker->GetClientID(),"%s tries to attack with %s weapon %s at %.2f range", attacker->GetName(),(weapon->GetIsRangeWeapon()?"range":"melee"),weapon->GetName(), attacker->RangeTo(target,false)); Debug3(LOG_COMBAT,attacker->GetClientID(),"%s started attacking with %s",attacker->GetName(), weapon->GetName()); // start the ball rolling QueueNextEvent(attacker,weaponSlot,target,attacker->GetClientID(),target->GetClientID()); startedAttacking=true; } else { if( weapon && attacker_character->GetActor()) { Debug3(LOG_COMBAT,attacker->GetClientID(),"%s tried attacking with %s but can't use it.", attacker->GetName(),weapon->GetName()); #ifdef COMBAT_DEBUG psserver->SendSystemError(attacker_character->GetActor()->GetClientID(), response); #endif } } } } /* Only notify the target if any attacks were able to start. Otherwise there are * no available weapons with which to attack. */ if (haveWeapon) { if (startedAttacking) { // The attacker should now enter combat mode if (attacker->GetMode() != PSCHARACTER_MODE_COMBAT) { SetCombat(attacker,stance); } } else { psserver->SendSystemError(attacker->GetClientID(),"You are too far away to attack!"); return; } } else { psserver->SendSystemError(attacker->GetClientID(),"You have no weapons equipped!"); return; } }