summaryrefslogtreecommitdiff
path: root/src/char/inter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/char/inter.c')
-rw-r--r--src/char/inter.c1133
1 files changed, 670 insertions, 463 deletions
diff --git a/src/char/inter.c b/src/char/inter.c
index 1ff2bca61..a7794c9c9 100644
--- a/src/char/inter.c
+++ b/src/char/inter.c
@@ -2,32 +2,35 @@
// See the LICENSE file
// Portions Copyright (c) Athena Dev Teams
-#include "../common/mmo.h"
-#include "../common/db.h"
-#include "../common/malloc.h"
-#include "../common/strlib.h"
-#include "../common/showmsg.h"
-#include "../common/socket.h"
-#include "../common/timer.h"
-#include "char.h"
+#define HERCULES_CORE
+
#include "inter.h"
-#include "int_party.h"
-#include "int_guild.h"
-#include "int_storage.h"
-#include "int_pet.h"
-#include "int_homun.h"
-#include "int_mercenary.h"
-#include "int_mail.h"
-#include "int_auction.h"
-#include "int_quest.h"
-#include "int_elemental.h"
+#include <errno.h>
#include <stdio.h>
-#include <string.h>
#include <stdlib.h>
-
+#include <string.h>
#include <sys/stat.h> // for stat/lstat/fstat - [Dekamaster/Ultimate GM Tool]
+#include "char.h"
+#include "int_auction.h"
+#include "int_elemental.h"
+#include "int_guild.h"
+#include "int_homun.h"
+#include "int_mail.h"
+#include "int_mercenary.h"
+#include "int_party.h"
+#include "int_pet.h"
+#include "int_quest.h"
+#include "int_storage.h"
+#include "../common/cbasetypes.h"
+#include "../common/db.h"
+#include "../common/malloc.h"
+#include "../common/mmo.h"
+#include "../common/showmsg.h"
+#include "../common/socket.h"
+#include "../common/strlib.h"
+#include "../common/timer.h"
#define WISDATA_TTL (60*1000) //Wis data Time To Live (60 seconds)
#define WISDELLIST_MAX 256 // Number of elements in the list Delete data Wis
@@ -42,33 +45,32 @@ char char_server_pw[32] = "ragnarok";
char char_server_db[32] = "ragnarok";
char default_codepage[32] = ""; //Feature by irmin.
-static struct accreg *accreg_pt;
unsigned int party_share_level = 10;
// recv. packet list
int inter_recv_packet_length[] = {
-1,-1, 7,-1, -1,13,36, (2 + 4 + 4 + 4 + NAME_LENGTH), 0, 0, 0, 0, 0, 0, 0, 0, // 3000-
- 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010-
- -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party
- -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030-
- -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040-
- -1,-1,10,10, 0,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus]
- 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish]
- -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil]
- 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080-
- -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator]
+ 6,-1, 0, 0, 0, 0, 0, 0, 10,-1, 0, 0, 0, 0, 0, 0, // 3010-
+ -1,10,-1,14, 14,19, 6,-1, 14,14, 0, 0, 0, 0, 0, 0, // 3020- Party
+ -1, 6,-1,-1, 55,19, 6,-1, 14,-1,-1,-1, 18,19,186,-1, // 3030-
+ -1, 9, 0, 0, 0, 0, 0, 0, 7, 6,10,10, 10,-1, 0, 0, // 3040-
+ -1,-1,10,10, 0,-1,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3050- Auction System [Zephyrus], Item Bound [Mhalicot]
+ 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3060- Quest system [Kevin] [Inkfish]
+ -1,10, 6,-1, 0, 0, 0, 0, 0, 0, 0, 0, -1,10, 6,-1, // 3070- Mercenary packets [Zephyrus], Elemental packets [pakpil]
+ 48,14,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3080-
+ -1,10,-1, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3090- Homunculus packets [albator]
};
struct WisData {
int id, fd, count, len;
- unsigned long tick;
+ int64 tick;
unsigned char src[24], dst[24], msg[512];
};
static DBMap* wis_db = NULL; // int wis_id -> struct WisData*
static int wis_dellist[WISDELLIST_MAX], wis_delnum;
-#define MAX_JOB_NAMES 106
-static char* msg_table[MAX_JOB_NAMES]; // messages 550 ~ 655 are job names
+#define MAX_JOB_NAMES 150
+static char* msg_table[MAX_JOB_NAMES]; // messages 550 ~ 699 are job names
const char* msg_txt(int msg_number) {
msg_number -= 550;
@@ -79,17 +81,24 @@ const char* msg_txt(int msg_number) {
return "Unknown";
}
-/*==========================================
- * Read Message Data -- at char server we only keep job names.
- *------------------------------------------*/
-int msg_config_read(const char* cfgName) {
+/**
+ * Reads Message Data.
+ *
+ * This is a modified version of the mapserver's msg_config_read to
+ * only read messages with IDs between 550 and 550+MAX_JOB_NAMES.
+ *
+ * @param[in] cfg_name configuration filename to read.
+ * @param[in] allow_override whether to allow duplicate message IDs to override the original value.
+ * @return success state.
+ */
+bool msg_config_read(const char *cfg_name, bool allow_override) {
int msg_number;
char line[1024], w1[1024], w2[1024];
FILE *fp;
static int called = 1;
- if ((fp = fopen(cfgName, "r")) == NULL) {
- ShowError("Messages file not found: %s\n", cfgName);
+ if ((fp = fopen(cfg_name, "r")) == NULL) {
+ ShowError("Messages file not found: %s\n", cfg_name);
return 1;
}
@@ -99,19 +108,25 @@ int msg_config_read(const char* cfgName) {
while(fgets(line, sizeof(line), fp) ) {
if (line[0] == '/' && line[1] == '/')
continue;
- if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
+ if (sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2) != 2)
continue;
if (strcmpi(w1, "import") == 0)
- msg_config_read(w2);
+ msg_config_read(w2, true);
else {
msg_number = atoi(w1);
if( msg_number < 550 || msg_number > (550+MAX_JOB_NAMES) )
continue;
msg_number -= 550;
if (msg_number >= 0 && msg_number < MAX_JOB_NAMES) {
- if (msg_table[msg_number] != NULL)
+ if (msg_table[msg_number] != NULL) {
+ if (!allow_override) {
+ ShowError("Duplicate message: ID '%d' was already used for '%s'. Message '%s' will be ignored.\n",
+ msg_number, w2, msg_table[msg_number]);
+ continue;
+ }
aFree(msg_table[msg_number]);
+ }
msg_table[msg_number] = (char *)aMalloc((strlen(w2) + 1)*sizeof (char));
strcpy(msg_table[msg_number],w2);
}
@@ -134,107 +149,107 @@ void do_final_msg(void) {
/* from pc.c due to @accinfo. any ideas to replace this crap are more than welcome. */
const char* job_name(int class_) {
switch (class_) {
- case JOB_NOVICE:
- case JOB_SWORDMAN:
- case JOB_MAGE:
- case JOB_ARCHER:
- case JOB_ACOLYTE:
- case JOB_MERCHANT:
- case JOB_THIEF:
+ case JOB_NOVICE: // 550
+ case JOB_SWORDMAN: // 551
+ case JOB_MAGE: // 552
+ case JOB_ARCHER: // 553
+ case JOB_ACOLYTE: // 554
+ case JOB_MERCHANT: // 555
+ case JOB_THIEF: // 556
return msg_txt(550 - JOB_NOVICE+class_);
- case JOB_KNIGHT:
- case JOB_PRIEST:
- case JOB_WIZARD:
- case JOB_BLACKSMITH:
- case JOB_HUNTER:
- case JOB_ASSASSIN:
+ case JOB_KNIGHT: // 557
+ case JOB_PRIEST: // 558
+ case JOB_WIZARD: // 559
+ case JOB_BLACKSMITH: // 560
+ case JOB_HUNTER: // 561
+ case JOB_ASSASSIN: // 562
return msg_txt(557 - JOB_KNIGHT+class_);
case JOB_KNIGHT2:
return msg_txt(557);
- case JOB_CRUSADER:
- case JOB_MONK:
- case JOB_SAGE:
- case JOB_ROGUE:
- case JOB_ALCHEMIST:
- case JOB_BARD:
- case JOB_DANCER:
+ case JOB_CRUSADER: // 563
+ case JOB_MONK: // 564
+ case JOB_SAGE: // 565
+ case JOB_ROGUE: // 566
+ case JOB_ALCHEMIST: // 567
+ case JOB_BARD: // 568
+ case JOB_DANCER: // 569
return msg_txt(563 - JOB_CRUSADER+class_);
case JOB_CRUSADER2:
return msg_txt(563);
- case JOB_WEDDING:
- case JOB_SUPER_NOVICE:
- case JOB_GUNSLINGER:
- case JOB_NINJA:
- case JOB_XMAS:
+ case JOB_WEDDING: // 570
+ case JOB_SUPER_NOVICE: // 571
+ case JOB_GUNSLINGER: // 572
+ case JOB_NINJA: // 573
+ case JOB_XMAS: // 574
return msg_txt(570 - JOB_WEDDING+class_);
case JOB_SUMMER:
return msg_txt(621);
- case JOB_NOVICE_HIGH:
- case JOB_SWORDMAN_HIGH:
- case JOB_MAGE_HIGH:
- case JOB_ARCHER_HIGH:
- case JOB_ACOLYTE_HIGH:
- case JOB_MERCHANT_HIGH:
- case JOB_THIEF_HIGH:
+ case JOB_NOVICE_HIGH: // 575
+ case JOB_SWORDMAN_HIGH: // 576
+ case JOB_MAGE_HIGH: // 577
+ case JOB_ARCHER_HIGH: // 578
+ case JOB_ACOLYTE_HIGH: // 579
+ case JOB_MERCHANT_HIGH: // 580
+ case JOB_THIEF_HIGH: // 581
return msg_txt(575 - JOB_NOVICE_HIGH+class_);
- case JOB_LORD_KNIGHT:
- case JOB_HIGH_PRIEST:
- case JOB_HIGH_WIZARD:
- case JOB_WHITESMITH:
- case JOB_SNIPER:
- case JOB_ASSASSIN_CROSS:
+ case JOB_LORD_KNIGHT: // 582
+ case JOB_HIGH_PRIEST: // 583
+ case JOB_HIGH_WIZARD: // 584
+ case JOB_WHITESMITH: // 585
+ case JOB_SNIPER: // 586
+ case JOB_ASSASSIN_CROSS: // 587
return msg_txt(582 - JOB_LORD_KNIGHT+class_);
case JOB_LORD_KNIGHT2:
return msg_txt(582);
- case JOB_PALADIN:
- case JOB_CHAMPION:
- case JOB_PROFESSOR:
- case JOB_STALKER:
- case JOB_CREATOR:
- case JOB_CLOWN:
- case JOB_GYPSY:
+ case JOB_PALADIN: // 588
+ case JOB_CHAMPION: // 589
+ case JOB_PROFESSOR: // 590
+ case JOB_STALKER: // 591
+ case JOB_CREATOR: // 592
+ case JOB_CLOWN: // 593
+ case JOB_GYPSY: // 594
return msg_txt(588 - JOB_PALADIN + class_);
case JOB_PALADIN2:
return msg_txt(588);
- case JOB_BABY:
- case JOB_BABY_SWORDMAN:
- case JOB_BABY_MAGE:
- case JOB_BABY_ARCHER:
- case JOB_BABY_ACOLYTE:
- case JOB_BABY_MERCHANT:
- case JOB_BABY_THIEF:
+ case JOB_BABY: // 595
+ case JOB_BABY_SWORDMAN: // 596
+ case JOB_BABY_MAGE: // 597
+ case JOB_BABY_ARCHER: // 598
+ case JOB_BABY_ACOLYTE: // 599
+ case JOB_BABY_MERCHANT: // 600
+ case JOB_BABY_THIEF: // 601
return msg_txt(595 - JOB_BABY + class_);
- case JOB_BABY_KNIGHT:
- case JOB_BABY_PRIEST:
- case JOB_BABY_WIZARD:
- case JOB_BABY_BLACKSMITH:
- case JOB_BABY_HUNTER:
- case JOB_BABY_ASSASSIN:
+ case JOB_BABY_KNIGHT: // 602
+ case JOB_BABY_PRIEST: // 603
+ case JOB_BABY_WIZARD: // 604
+ case JOB_BABY_BLACKSMITH: // 605
+ case JOB_BABY_HUNTER: // 606
+ case JOB_BABY_ASSASSIN: // 607
return msg_txt(602 - JOB_BABY_KNIGHT + class_);
case JOB_BABY_KNIGHT2:
return msg_txt(602);
- case JOB_BABY_CRUSADER:
- case JOB_BABY_MONK:
- case JOB_BABY_SAGE:
- case JOB_BABY_ROGUE:
- case JOB_BABY_ALCHEMIST:
- case JOB_BABY_BARD:
- case JOB_BABY_DANCER:
+ case JOB_BABY_CRUSADER: // 608
+ case JOB_BABY_MONK: // 609
+ case JOB_BABY_SAGE: // 610
+ case JOB_BABY_ROGUE: // 611
+ case JOB_BABY_ALCHEMIST: // 612
+ case JOB_BABY_BARD: // 613
+ case JOB_BABY_DANCER: // 614
return msg_txt(608 - JOB_BABY_CRUSADER + class_);
case JOB_BABY_CRUSADER2:
@@ -251,74 +266,82 @@ const char* job_name(int class_) {
case JOB_SOUL_LINKER:
return msg_txt(618);
- case JOB_GANGSI:
- case JOB_DEATH_KNIGHT:
- case JOB_DARK_COLLECTOR:
+ case JOB_GANGSI: // 622
+ case JOB_DEATH_KNIGHT: // 623
+ case JOB_DARK_COLLECTOR: // 624
return msg_txt(622 - JOB_GANGSI+class_);
- case JOB_RUNE_KNIGHT:
- case JOB_WARLOCK:
- case JOB_RANGER:
- case JOB_ARCH_BISHOP:
- case JOB_MECHANIC:
- case JOB_GUILLOTINE_CROSS:
+ case JOB_RUNE_KNIGHT: // 625
+ case JOB_WARLOCK: // 626
+ case JOB_RANGER: // 627
+ case JOB_ARCH_BISHOP: // 628
+ case JOB_MECHANIC: // 629
+ case JOB_GUILLOTINE_CROSS: // 630
return msg_txt(625 - JOB_RUNE_KNIGHT+class_);
- case JOB_RUNE_KNIGHT_T:
- case JOB_WARLOCK_T:
- case JOB_RANGER_T:
- case JOB_ARCH_BISHOP_T:
- case JOB_MECHANIC_T:
- case JOB_GUILLOTINE_CROSS_T:
- return msg_txt(625 - JOB_RUNE_KNIGHT_T+class_);
-
- case JOB_ROYAL_GUARD:
- case JOB_SORCERER:
- case JOB_MINSTREL:
- case JOB_WANDERER:
- case JOB_SURA:
- case JOB_GENETIC:
- case JOB_SHADOW_CHASER:
+ case JOB_RUNE_KNIGHT_T: // 656
+ case JOB_WARLOCK_T: // 657
+ case JOB_RANGER_T: // 658
+ case JOB_ARCH_BISHOP_T: // 659
+ case JOB_MECHANIC_T: // 660
+ case JOB_GUILLOTINE_CROSS_T: // 661
+ return msg_txt(656 - JOB_RUNE_KNIGHT_T+class_);
+
+ case JOB_ROYAL_GUARD: // 631
+ case JOB_SORCERER: // 632
+ case JOB_MINSTREL: // 633
+ case JOB_WANDERER: // 634
+ case JOB_SURA: // 635
+ case JOB_GENETIC: // 636
+ case JOB_SHADOW_CHASER: // 637
return msg_txt(631 - JOB_ROYAL_GUARD+class_);
- case JOB_ROYAL_GUARD_T:
- case JOB_SORCERER_T:
- case JOB_MINSTREL_T:
- case JOB_WANDERER_T:
- case JOB_SURA_T:
- case JOB_GENETIC_T:
- case JOB_SHADOW_CHASER_T:
- return msg_txt(631 - JOB_ROYAL_GUARD_T+class_);
+ case JOB_ROYAL_GUARD_T: // 662
+ case JOB_SORCERER_T: // 663
+ case JOB_MINSTREL_T: // 664
+ case JOB_WANDERER_T: // 665
+ case JOB_SURA_T: // 666
+ case JOB_GENETIC_T: // 667
+ case JOB_SHADOW_CHASER_T: // 668
+ return msg_txt(662 - JOB_ROYAL_GUARD_T+class_);
case JOB_RUNE_KNIGHT2:
- case JOB_RUNE_KNIGHT_T2:
return msg_txt(625);
+ case JOB_RUNE_KNIGHT_T2:
+ return msg_txt(656);
+
case JOB_ROYAL_GUARD2:
- case JOB_ROYAL_GUARD_T2:
return msg_txt(631);
+ case JOB_ROYAL_GUARD_T2:
+ return msg_txt(662);
+
case JOB_RANGER2:
- case JOB_RANGER_T2:
return msg_txt(627);
+ case JOB_RANGER_T2:
+ return msg_txt(658);
+
case JOB_MECHANIC2:
- case JOB_MECHANIC_T2:
return msg_txt(629);
- case JOB_BABY_RUNE:
- case JOB_BABY_WARLOCK:
- case JOB_BABY_RANGER:
- case JOB_BABY_BISHOP:
- case JOB_BABY_MECHANIC:
- case JOB_BABY_CROSS:
- case JOB_BABY_GUARD:
- case JOB_BABY_SORCERER:
- case JOB_BABY_MINSTREL:
- case JOB_BABY_WANDERER:
- case JOB_BABY_SURA:
- case JOB_BABY_GENETIC:
- case JOB_BABY_CHASER:
+ case JOB_MECHANIC_T2:
+ return msg_txt(660);
+
+ case JOB_BABY_RUNE: // 638
+ case JOB_BABY_WARLOCK: // 639
+ case JOB_BABY_RANGER: // 640
+ case JOB_BABY_BISHOP: // 641
+ case JOB_BABY_MECHANIC: // 642
+ case JOB_BABY_CROSS: // 643
+ case JOB_BABY_GUARD: // 644
+ case JOB_BABY_SORCERER: // 645
+ case JOB_BABY_MINSTREL: // 646
+ case JOB_BABY_WANDERER: // 647
+ case JOB_BABY_SURA: // 648
+ case JOB_BABY_GENETIC: // 649
+ case JOB_BABY_CHASER: // 650
return msg_txt(638 - JOB_BABY_RUNE+class_);
case JOB_BABY_RUNE2:
@@ -333,23 +356,28 @@ const char* job_name(int class_) {
case JOB_BABY_MECHANIC2:
return msg_txt(642);
- case JOB_SUPER_NOVICE_E:
- case JOB_SUPER_BABY_E:
+ case JOB_SUPER_NOVICE_E: // 651
+ case JOB_SUPER_BABY_E: // 652
return msg_txt(651 - JOB_SUPER_NOVICE_E+class_);
- case JOB_KAGEROU:
- case JOB_OBORO:
+ case JOB_KAGEROU: // 653
+ case JOB_OBORO: // 654
return msg_txt(653 - JOB_KAGEROU+class_);
- default:
+ case JOB_REBELLION:
return msg_txt(655);
+
+ default:
+ return msg_txt(620); // "Unknown Job"
}
}
-/**
- * [Dekamaster/Nightroad]
- **/
-const char * geoip_countryname[253] = {"Unknown","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
+/* [Dekamaster/Nightroad] */
+#define GEOIP_MAX_COUNTRIES 255
+#define GEOIP_STRUCTURE_INFO_MAX_SIZE 20
+#define GEOIP_COUNTRY_BEGIN 16776960
+
+const char * geoip_countryname[GEOIP_MAX_COUNTRIES] = {"Unknown","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles",
"Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados",
"Bangladesh","Belgium","Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia",
"Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the",
@@ -374,17 +402,15 @@ const char * geoip_countryname[253] = {"Unknown","Asia/Pacific Region","Europe",
"Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines","Venezuela",
"Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa",
"Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider","Other","Aland Islands","Guernsey","Isle of Man","Jersey",
- "Saint Barthelemy","Saint Martin"};
-unsigned char *geoip_cache;
-void geoip_readdb(void){
- struct stat bufa;
- FILE *db=fopen("./db/GeoIP.dat","rb");
- fstat(fileno(db), &bufa);
- geoip_cache = (unsigned char *) malloc(sizeof(unsigned char) * bufa.st_size);
- if(fread(geoip_cache, sizeof(unsigned char), bufa.st_size, db) != bufa.st_size) { ShowError("geoip_cache reading didn't read all elements \n"); }
- fclose(db);
- ShowStatus("Finished Reading "CL_GREEN"GeoIP"CL_RESET" Database.\n");
-}
+ "Saint Barthelemy", "Saint Martin", "Bonaire, Saint Eustatius and Saba", "South Sudan"};
+/**
+ * GeoIP information
+ **/
+struct s_geoip {
+ unsigned char *cache; // GeoIP.dat information see geoip_init()
+ bool active;
+} geoip;
+
/* [Dekamaster/Nightroad] */
/* WHY NOT A DBMAP: There are millions of entries in GeoIP and it has its own algorithm to go quickly through them, a DBMap wouldn't be efficient */
const char* geoip_getcountry(uint32 ipnum){
@@ -393,8 +419,11 @@ const char* geoip_getcountry(uint32 ipnum){
const unsigned char *buf;
unsigned int offset = 0;
+ if( geoip.active == false )
+ return geoip_countryname[0];
+
for (depth = 31; depth >= 0; depth--) {
- buf = geoip_cache + (long)6 *offset;
+ buf = geoip.cache + (long)6 *offset;
if (ipnum & (1 << depth)) {
/* Take the right-hand branch */
x = (buf[3*1 + 0] << (0*8))
@@ -406,24 +435,111 @@ const char* geoip_getcountry(uint32 ipnum){
+ (buf[3*0 + 1] << (1*8))
+ (buf[3*0 + 2] << (2*8));
}
- if (x >= 16776960) {
- x=x-16776960;
+ if (x >= GEOIP_COUNTRY_BEGIN) {
+ x = x-GEOIP_COUNTRY_BEGIN;
+
+ if( x > GEOIP_MAX_COUNTRIES )
+ return geoip_countryname[0];
+
return geoip_countryname[x];
}
offset = x;
}
+ ShowError("geoip_getcountry(): Error traversing database for ipnum %d\n", ipnum);
+ ShowWarning("geoip_getcountry(): Possible database corruption!\n");
+
return geoip_countryname[0];
}
-/* sends a mesasge to map server (fd) to a user (u_fd) although we use fd we keep aid for safe-check */
-/* extremely handy I believe it will serve other uses in the near future */
-void inter_to_fd(int fd, int u_fd, int aid, char* msg, ...) {
+
+/**
+ * Disables GeoIP
+ * frees geoip.cache
+ **/
+void geoip_final(bool shutdown) {
+ if (geoip.cache) {
+ aFree(geoip.cache);
+ geoip.cache = NULL;
+ }
+
+ if (geoip.active) {
+ if (!shutdown)
+ ShowStatus("GeoIP "CL_RED"disabled"CL_RESET".\n");
+ geoip.active = false;
+ }
+}
+
+/**
+ * Reads GeoIP database and stores it into memory
+ * geoip.cache should be freed after use!
+ * http://dev.maxmind.com/geoip/legacy/geolite/
+ **/
+void geoip_init(void) {
+ int i, fno;
+ char db_type = 1;
+ unsigned char delim[3];
+ struct stat bufa;
+ FILE *db;
+
+ geoip.active = true;
+
+ db = fopen("./db/GeoIP.dat","rb");
+ if( db == NULL ) {
+ ShowError("geoip_readdb: Error reading GeoIP.dat!\n");
+ geoip_final(false);
+ return;
+ }
+ fno = fileno(db);
+ if( fstat(fno, &bufa) < 0 ) {
+ ShowError("geoip_readdb: Error stating GeoIP.dat! Error %d\n", errno);
+ geoip_final(false);
+ return;
+ }
+ geoip.cache = aMalloc( (sizeof(geoip.cache) * bufa.st_size) );
+ if( fread(geoip.cache, sizeof(unsigned char), bufa.st_size, db) != bufa.st_size ) {
+ ShowError("geoip_cache: Couldn't read all elements!\n");
+ fclose(db);
+ geoip_final(false);
+ return;
+ }
+
+ // Search database type
+ fseek(db, -3l, SEEK_END);
+ for( i = 0; i < GEOIP_STRUCTURE_INFO_MAX_SIZE; i++ ) {
+ fread(delim, sizeof(delim[0]), 3, db);
+ if( delim[0] == 255 && delim[1] == 255 && delim[2] == 255 ) {
+ fread(&db_type, sizeof(db_type), 1, db);
+ break;
+ } else {
+ fseek(db, -4l, SEEK_CUR);
+ }
+ }
+
+ fclose(db);
+
+ if( db_type != 1 ) {
+ if( db_type )
+ ShowError("geoip_init(): Database type is not supported %d!\n", db_type);
+ else
+ ShowError("geoip_init(): GeoIP is corrupted!\n");
+
+ geoip_final(false);
+ return;
+ }
+ ShowStatus("Finished Reading "CL_GREEN"GeoIP"CL_RESET" Database.\n");
+}
+
+/**
+ * Argument-list version of inter_msg_to_fd
+ * @see inter_msg_to_fd
+ */
+void inter_vmsg_to_fd(int fd, int u_fd, int aid, char* msg, va_list ap) {
char msg_out[512];
- va_list ap;
+ va_list apcopy;
int len = 1;/* yes we start at 1 */
- va_start(ap,msg);
- len += vsnprintf(msg_out, 512, msg, ap);
- va_end(ap);
+ va_copy(apcopy, ap);
+ len += vsnprintf(msg_out, 512, msg, apcopy);
+ va_end(apcopy);
WFIFOHEAD(fd,12 + len);
@@ -437,6 +553,24 @@ void inter_to_fd(int fd, int u_fd, int aid, char* msg, ...) {
return;
}
+
+/**
+ * Sends a message to map server (fd) to a user (u_fd) although we use fd we
+ * keep aid for safe-check.
+ * @param fd Mapserver's fd
+ * @param u_fd Recipient's fd
+ * @param aid Recipient's expected for sanity checks on the mapserver
+ * @param msg Message format string
+ * @param ... Additional parameters for (v)sprinf
+ */
+void inter_msg_to_fd(int fd, int u_fd, int aid, char *msg, ...) __attribute__((format(printf, 4, 5)));
+void inter_msg_to_fd(int fd, int u_fd, int aid, char *msg, ...) {
+ va_list ap;
+ va_start(ap,msg);
+ inter_vmsg_to_fd(fd, u_fd, aid, msg, ap);
+ va_end(ap);
+}
+
/* [Dekamaster/Nightroad] */
void mapif_parse_accinfo(int fd) {
int u_fd = RFIFOL(fd,2), aid = RFIFOL(fd,6), castergroup = RFIFOL(fd,10);
@@ -451,13 +585,13 @@ void mapif_parse_accinfo(int fd) {
account_id = atoi(query);
if (account_id < START_ACCOUNT_NUM) { // is string
- if ( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `char` WHERE `name` LIKE '%s' LIMIT 10", query_esq)
+ if ( SQL_ERROR == SQL->Query(sql_handle, "SELECT `account_id`,`name`,`class`,`base_level`,`job_level`,`online` FROM `%s` WHERE `name` LIKE '%s' LIMIT 10", char_db, query_esq)
|| SQL->NumRows(sql_handle) == 0 ) {
if( SQL->NumRows(sql_handle) == 0 ) {
- inter_to_fd(fd, u_fd, aid, "No matches were found for your criteria, '%s'",query);
+ inter_msg_to_fd(fd, u_fd, aid, "No matches were found for your criteria, '%s'",query);
} else {
Sql_ShowDebug(sql_handle);
- inter_to_fd(fd, u_fd, aid, "An error occured, bother your admin about it.");
+ inter_msg_to_fd(fd, u_fd, aid, "An error occurred, bother your admin about it.");
}
SQL->FreeResult(sql_handle);
return;
@@ -467,7 +601,7 @@ void mapif_parse_accinfo(int fd) {
SQL->GetData(sql_handle, 0, &data, NULL); account_id = atoi(data);
SQL->FreeResult(sql_handle);
} else {// more than one, listing... [Dekamaster/Nightroad]
- inter_to_fd(fd, u_fd, aid, "Your query returned the following %d results, please be more specific...",(int)SQL->NumRows(sql_handle));
+ inter_msg_to_fd(fd, u_fd, aid, "Your query returned the following %d results, please be more specific...",(int)SQL->NumRows(sql_handle));
while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) {
int class_;
short base_level, job_level, online;
@@ -480,7 +614,7 @@ void mapif_parse_accinfo(int fd) {
SQL->GetData(sql_handle, 4, &data, NULL); job_level = atoi(data);
SQL->GetData(sql_handle, 5, &data, NULL); online = atoi(data);
- inter_to_fd(fd, u_fd, aid, "[AID: %d] %s | %s | Level: %d/%d | %s", account_id, name, job_name(class_), base_level, job_level, online?"Online":"Offline");
+ inter_msg_to_fd(fd, u_fd, aid, "[AID: %d] %s | %s | Level: %d/%d | %s", account_id, name, job_name(class_), base_level, job_level, online?"Online":"Offline");
}
SQL->FreeResult(sql_handle);
return;
@@ -489,206 +623,287 @@ void mapif_parse_accinfo(int fd) {
}
/* it will only get here if we have a single match */
+ /* and we will send packet with account id to login server asking for account info */
if( account_id ) {
- char userid[NAME_LENGTH], user_pass[NAME_LENGTH], email[40], last_ip[20], lastlogin[30], pincode[5], birthdate[11];
- short level = -1;
- int logincount = 0,state = 0;
- if ( SQL_ERROR == SQL->Query(sql_handle, "SELECT `userid`, `user_pass`, `email`, `last_ip`, `group_id`, `lastlogin`, `logincount`, `state`,`pincode`,`birthdate` FROM `login` WHERE `account_id` = '%d' LIMIT 1", account_id)
- || SQL->NumRows(sql_handle) == 0 ) {
- if( SQL->NumRows(sql_handle) == 0 ) {
- inter_to_fd(fd, u_fd, aid, "No account with ID '%d' was found.", account_id );
- } else {
- inter_to_fd(fd, u_fd, aid, "An error occured, bother your admin about it.");
- Sql_ShowDebug(sql_handle);
- }
- } else {
- SQL->NextRow(sql_handle);
- SQL->GetData(sql_handle, 0, &data, NULL); safestrncpy(userid, data, sizeof(userid));
- SQL->GetData(sql_handle, 1, &data, NULL); safestrncpy(user_pass, data, sizeof(user_pass));
- SQL->GetData(sql_handle, 2, &data, NULL); safestrncpy(email, data, sizeof(email));
- SQL->GetData(sql_handle, 3, &data, NULL); safestrncpy(last_ip, data, sizeof(last_ip));
- SQL->GetData(sql_handle, 4, &data, NULL); level = atoi(data);
- SQL->GetData(sql_handle, 5, &data, NULL); safestrncpy(lastlogin, data, sizeof(lastlogin));
- SQL->GetData(sql_handle, 6, &data, NULL); logincount = atoi(data);
- SQL->GetData(sql_handle, 7, &data, NULL); state = atoi(data);
- SQL->GetData(sql_handle, 8, &data, NULL); safestrncpy(pincode, data, sizeof(pincode));
- SQL->GetData(sql_handle, 9, &data, NULL); safestrncpy(birthdate, data, sizeof(birthdate));
- }
-
- SQL->FreeResult(sql_handle);
-
- if (level == -1)
- return;
-
- inter_to_fd(fd, u_fd, aid, "-- Account %d --", account_id );
- inter_to_fd(fd, u_fd, aid, "User: %s | GM Group: %d | State: %d", userid, level, state );
-
- if (level < castergroup) { /* only show pass if your gm level is greater than the one you're searching for */
- if( strlen(pincode) )
- inter_to_fd(fd, u_fd, aid, "Password: %s (PIN:%s)", user_pass, pincode );
- else
- inter_to_fd(fd, u_fd, aid, "Password: %s", user_pass );
- }
-
- inter_to_fd(fd, u_fd, aid, "Account e-mail: %s | Birthdate: %s", email, birthdate);
- inter_to_fd(fd, u_fd, aid, "Last IP: %s (%s)", last_ip, geoip_getcountry(str2ip(last_ip)) );
- inter_to_fd(fd, u_fd, aid, "This user has logged %d times, the last time were at %s", logincount, lastlogin );
- inter_to_fd(fd, u_fd, aid, "-- Character Details --" );
-
-
- if ( SQL_ERROR == SQL->Query(sql_handle, "SELECT `char_id`, `name`, `char_num`, `class`, `base_level`, `job_level`, `online` FROM `char` WHERE `account_id` = '%d' ORDER BY `char_num` LIMIT %d", account_id, MAX_CHARS)
- || SQL->NumRows(sql_handle) == 0 ) {
-
- if( SQL->NumRows(sql_handle) == 0 )
- inter_to_fd(fd, u_fd, aid,"This account doesn't have characters.");
- else {
- inter_to_fd(fd, u_fd, aid,"An error occured, bother your admin about it.");
- Sql_ShowDebug(sql_handle);
- }
-
- } else {
- while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) {
- int char_id, class_;
- short char_num, base_level, job_level, online;
- char name[NAME_LENGTH];
-
- SQL->GetData(sql_handle, 0, &data, NULL); char_id = atoi(data);
- SQL->GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name));
- SQL->GetData(sql_handle, 2, &data, NULL); char_num = atoi(data);
- SQL->GetData(sql_handle, 3, &data, NULL); class_ = atoi(data);
- SQL->GetData(sql_handle, 4, &data, NULL); base_level = atoi(data);
- SQL->GetData(sql_handle, 5, &data, NULL); job_level = atoi(data);
- SQL->GetData(sql_handle, 6, &data, NULL); online = atoi(data);
-
- inter_to_fd(fd, u_fd, aid, "[Slot/CID: %d/%d] %s | %s | Level: %d/%d | %s", char_num, char_id, name, job_name(class_), base_level, job_level, online?"On":"Off");
- }
- }
- SQL->FreeResult(sql_handle);
+ mapif_on_parse_accinfo(account_id, u_fd, aid, castergroup, fd);
}
return;
}
-//--------------------------------------------------------
-// Save registry to sql
-int inter_accreg_tosql(int account_id, int char_id, struct accreg* reg, int type)
-{
- struct global_reg* r;
- StringBuf buf;
- int i;
-
- if( account_id <= 0 )
- return 0;
- reg->account_id = account_id;
- reg->char_id = char_id;
+void mapif_parse_accinfo2(bool success, int map_fd, int u_fd, int u_aid, int account_id, const char *userid, const char *user_pass, const char *email, const char *last_ip, const char *lastlogin, const char *pin_code, const char *birthdate, int group_id, int logincount, int state) {
+ if (map_fd <= 0 || !session_isActive(map_fd))
+ return; // check if we have a valid fd
- //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
- switch( type )
- {
- case 3: //Char Reg
- if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) )
- Sql_ShowDebug(sql_handle);
- account_id = 0;
- break;
- case 2: //Account Reg
- if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'", reg_db, account_id) )
- Sql_ShowDebug(sql_handle);
- char_id = 0;
- break;
- case 1: //Account2 Reg
- ShowError("inter_accreg_tosql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
- return 0;
- default:
- ShowError("inter_accreg_tosql: Invalid type %d\n", type);
- return 0;
+ if (!success) {
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "No account with ID '%d' was found.", account_id);
+ return;
}
- if( reg->reg_num <= 0 )
- return 0;
-
- StrBuf->Init(&buf);
- StrBuf->Printf(&buf, "INSERT INTO `%s` (`type`,`account_id`,`char_id`,`str`,`value`) VALUES ", reg_db);
-
- for( i = 0; i < reg->reg_num; ++i ) {
- r = &reg->reg[i];
- if( r->str[0] != '\0' && r->value[0] != '\0' ) {
- char str[32];
- char val[256];
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "-- Account %d --", account_id);
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "User: %s | GM Group: %d | State: %d", userid, group_id, state);
- if( i > 0 )
- StrBuf->AppendStr(&buf, ",");
+ if (user_pass && *user_pass != '\0') { /* password is only received if your gm level is greater than the one you're searching for */
+ if (pin_code && *pin_code != '\0')
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "Password: %s (PIN:%s)", user_pass, pin_code);
+ else
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "Password: %s", user_pass );
+ }
- SQL->EscapeString(sql_handle, str, r->str);
- SQL->EscapeString(sql_handle, val, r->value);
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "Account e-mail: %s | Birthdate: %s", email, birthdate);
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "Last IP: %s (%s)", last_ip, geoip_getcountry(str2ip(last_ip)));
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "This user has logged %d times, the last time were at %s", logincount, lastlogin);
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "-- Character Details --");
- StrBuf->Printf(&buf, "('%d','%d','%d','%s','%s')", type, account_id, char_id, str, val);
+ if ( SQL_ERROR == SQL->Query(sql_handle, "SELECT `char_id`, `name`, `char_num`, `class`, `base_level`, `job_level`, `online` "
+ "FROM `%s` WHERE `account_id` = '%d' ORDER BY `char_num` LIMIT %d", char_db, account_id, MAX_CHARS)
+ || SQL->NumRows(sql_handle) == 0 ) {
+ if (SQL->NumRows(sql_handle) == 0) {
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "This account doesn't have characters.");
+ } else {
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "An error occurred, bother your admin about it.");
+ Sql_ShowDebug(sql_handle);
+ }
+ } else {
+ while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) {
+ char *data;
+ int char_id, class_;
+ short char_num, base_level, job_level, online;
+ char name[NAME_LENGTH];
+
+ SQL->GetData(sql_handle, 0, &data, NULL); char_id = atoi(data);
+ SQL->GetData(sql_handle, 1, &data, NULL); safestrncpy(name, data, sizeof(name));
+ SQL->GetData(sql_handle, 2, &data, NULL); char_num = atoi(data);
+ SQL->GetData(sql_handle, 3, &data, NULL); class_ = atoi(data);
+ SQL->GetData(sql_handle, 4, &data, NULL); base_level = atoi(data);
+ SQL->GetData(sql_handle, 5, &data, NULL); job_level = atoi(data);
+ SQL->GetData(sql_handle, 6, &data, NULL); online = atoi(data);
+
+ inter_msg_to_fd(map_fd, u_fd, u_aid, "[Slot/CID: %d/%d] %s | %s | Level: %d/%d | %s", char_num, char_id, name, job_name(class_), base_level, job_level, online?"On":"Off");
}
}
+ SQL->FreeResult(sql_handle);
- if( SQL_ERROR == SQL->QueryStr(sql_handle, StrBuf->Value(&buf)) ) {
- Sql_ShowDebug(sql_handle);
+ return;
+}
+/**
+ * Handles save reg data from map server and distributes accordingly.
+ *
+ * @param val either str or int, depending on type
+ * @param type false when int, true otherwise
+ **/
+void inter_savereg(int account_id, int char_id, const char *key, unsigned int index, intptr_t val, bool is_string) {
+ /* to login server we go! */
+ if( key[0] == '#' && key[1] == '#' ) {/* global account reg */
+ global_accreg_to_login_add(key,index,val,is_string);
+ } else if ( key[0] == '#' ) {/* local account reg */
+ if( is_string ) {
+ if( val ) {
+ if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", acc_reg_str_db, account_id, key, index, (char*)val) )
+ Sql_ShowDebug(sql_handle);
+ } else {
+ if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", acc_reg_str_db, account_id, key, index) )
+ Sql_ShowDebug(sql_handle);
+ }
+ } else {
+ if( val ) {
+ if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`account_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%d')", acc_reg_num_db, account_id, key, index, (int)val) )
+ Sql_ShowDebug(sql_handle);
+ } else {
+ if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `account_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", acc_reg_num_db, account_id, key, index) )
+ Sql_ShowDebug(sql_handle);
+ }
+ }
+ } else { /* char reg */
+ if( is_string ) {
+ if( val ) {
+ if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`char_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%s')", char_reg_str_db, char_id, key, index, (char*)val) )
+ Sql_ShowDebug(sql_handle);
+ } else {
+ if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", char_reg_str_db, char_id, key, index) )
+ Sql_ShowDebug(sql_handle);
+ }
+ } else {
+ if( val ) {
+ if( SQL_ERROR == SQL->Query(sql_handle, "REPLACE INTO `%s` (`char_id`,`key`,`index`,`value`) VALUES ('%d','%s','%u','%d')", char_reg_num_db, char_id, key, index, (int)val) )
+ Sql_ShowDebug(sql_handle);
+ } else {
+ if( SQL_ERROR == SQL->Query(sql_handle, "DELETE FROM `%s` WHERE `char_id` = '%d' AND `key` = '%s' AND `index` = '%u' LIMIT 1", char_reg_num_db, char_id, key, index) )
+ Sql_ShowDebug(sql_handle);
+ }
+ }
}
-
- StrBuf->Destroy(&buf);
-
- return 1;
+
}
// Load account_reg from sql (type=2)
-int inter_accreg_fromsql(int account_id,int char_id, struct accreg *reg, int type)
+int inter_accreg_fromsql(int account_id,int char_id, int fd, int type)
{
- struct global_reg* r;
char* data;
size_t len;
- int i;
+ unsigned int plen = 0;
- if( reg == NULL)
- return 0;
-
- memset(reg, 0, sizeof(struct accreg));
- reg->account_id = account_id;
- reg->char_id = char_id;
+ switch( type ) {
+ case 3: //char reg
+ if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `char_id`='%d'", char_reg_str_db, char_id) )
+ Sql_ShowDebug(sql_handle);
+ break;
+ case 2: //account reg
+ if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", acc_reg_str_db, account_id) )
+ Sql_ShowDebug(sql_handle);
+ break;
+ case 1: //account2 reg
+ ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
+ return 0;
+ default:
+ ShowError("inter_accreg_fromsql: Invalid type %d\n", type);
+ return 0;
+ }
+
+ WFIFOHEAD(fd, 60000 + 300);
+ WFIFOW(fd, 0) = 0x3804;
+ /* 0x2 = length, set prior to being sent */
+ WFIFOL(fd, 4) = account_id;
+ WFIFOL(fd, 8) = char_id;
+ WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */
+ WFIFOB(fd, 13) = 1;/* is string type */
+ WFIFOW(fd, 14) = 0;/* count */
+ plen = 16;
+
+ /**
+ * Vessel!
+ *
+ * str type
+ * { keyLength(B), key(<keyLength>), index(L), valLength(B), val(<valLength>) }
+ **/
+ while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) {
+
+ SQL->GetData(sql_handle, 0, &data, NULL);
+ len = strlen(data)+1;
+
+ WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */
+ plen += 1;
+
+ safestrncpy((char*)WFIFOP(fd,plen), data, len);
+ plen += len;
+
+ SQL->GetData(sql_handle, 1, &data, NULL);
+
+ WFIFOL(fd, plen) = (unsigned int)atol(data);
+ plen += 4;
+
+ SQL->GetData(sql_handle, 2, &data, NULL);
+ len = strlen(data)+1;
+
+ WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 254 */
+ plen += 1;
+
+ safestrncpy((char*)WFIFOP(fd,plen), data, len);
+ plen += len;
+
+ WFIFOW(fd, 14) += 1;
+
+ if( plen > 60000 ) {
+ WFIFOW(fd, 2) = plen;
+ WFIFOSET(fd, plen);
+
+ /* prepare follow up */
+ WFIFOHEAD(fd, 60000 + 300);
+ WFIFOW(fd, 0) = 0x3804;
+ /* 0x2 = length, set prior to being sent */
+ WFIFOL(fd, 4) = account_id;
+ WFIFOL(fd, 8) = char_id;
+ WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */
+ WFIFOB(fd, 13) = 1;/* is string type */
+ WFIFOW(fd, 14) = 0;/* count */
+ plen = 16;
+ }
+ }
+
+ /* mark & go. */
+ WFIFOW(fd, 2) = plen;
+ WFIFOSET(fd, plen);
- //`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
- switch( type )
- {
- case 3: //char reg
- if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=3 AND `char_id`='%d'", reg_db, char_id) )
- Sql_ShowDebug(sql_handle);
- break;
- case 2: //account reg
- if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'", reg_db, account_id) )
- Sql_ShowDebug(sql_handle);
- break;
- case 1: //account2 reg
- ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
- return 0;
- default:
- ShowError("inter_accreg_fromsql: Invalid type %d\n", type);
- return 0;
+ SQL->FreeResult(sql_handle);
+
+ switch( type ) {
+ case 3: //char reg
+ if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `char_id`='%d'", char_reg_num_db, char_id) )
+ Sql_ShowDebug(sql_handle);
+ break;
+ case 2: //account reg
+ if( SQL_ERROR == SQL->Query(sql_handle, "SELECT `key`, `index`, `value` FROM `%s` WHERE `account_id`='%d'", acc_reg_num_db, account_id) )
+ Sql_ShowDebug(sql_handle);
+ break;
+ case 1: //account2 reg
+ ShowError("inter_accreg_fromsql: Char server shouldn't handle type 1 registry values (##). That is the login server's work!\n");
+ return 0;
}
- for( i = 0; i < MAX_REG_NUM && SQL_SUCCESS == SQL->NextRow(sql_handle); ++i )
- {
- r = &reg->reg[i];
- // str
- SQL->GetData(sql_handle, 0, &data, &len);
- memcpy(r->str, data, min(len, sizeof(r->str)));
- // value
- SQL->GetData(sql_handle, 1, &data, &len);
- memcpy(r->value, data, min(len, sizeof(r->value)));
+
+ WFIFOHEAD(fd, 60000 + 300);
+ WFIFOW(fd, 0) = 0x3804;
+ /* 0x2 = length, set prior to being sent */
+ WFIFOL(fd, 4) = account_id;
+ WFIFOL(fd, 8) = char_id;
+ WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */
+ WFIFOB(fd, 13) = 0;/* is int type */
+ WFIFOW(fd, 14) = 0;/* count */
+ plen = 16;
+
+ /**
+ * Vessel!
+ *
+ * int type
+ * { keyLength(B), key(<keyLength>), index(L), value(L) }
+ **/
+ while ( SQL_SUCCESS == SQL->NextRow(sql_handle) ) {
+
+ SQL->GetData(sql_handle, 0, &data, NULL);
+ len = strlen(data)+1;
+
+ WFIFOB(fd, plen) = (unsigned char)len;/* won't be higher; the column size is 32 */
+ plen += 1;
+
+ safestrncpy((char*)WFIFOP(fd,plen), data, len);
+ plen += len;
+
+ SQL->GetData(sql_handle, 1, &data, NULL);
+
+ WFIFOL(fd, plen) = (unsigned int)atol(data);
+ plen += 4;
+
+ SQL->GetData(sql_handle, 2, &data, NULL);
+
+ WFIFOL(fd, plen) = atoi(data);
+ plen += 4;
+
+ WFIFOW(fd, 14) += 1;
+
+ if( plen > 60000 ) {
+ WFIFOW(fd, 2) = plen;
+ WFIFOSET(fd, plen);
+
+ /* prepare follow up */
+ WFIFOHEAD(fd, 60000 + 300);
+ WFIFOW(fd, 0) = 0x3804;
+ /* 0x2 = length, set prior to being sent */
+ WFIFOL(fd, 4) = account_id;
+ WFIFOL(fd, 8) = char_id;
+ WFIFOB(fd, 12) = 0;/* var type (only set when all vars have been sent, regardless of type) */
+ WFIFOB(fd, 13) = 0;/* is int type */
+ WFIFOW(fd, 14) = 0;/* count */
+ plen = 16;
+ }
}
- reg->reg_num = i;
+
+ /* mark as complete & go. */
+ WFIFOB(fd, 12) = type;
+ WFIFOW(fd, 2) = plen;
+ WFIFOSET(fd, plen);
+
SQL->FreeResult(sql_handle);
return 1;
}
-// Initialize
-int inter_accreg_sql_init(void)
-{
- CREATE(accreg_pt, struct accreg, 1);
- return 0;
-
-}
-
/*==========================================
* read config file
*------------------------------------------*/
@@ -704,9 +919,8 @@ static int inter_config_read(const char* cfgName)
return 1;
}
- while(fgets(line, sizeof(line), fp))
- {
- i = sscanf(line, "%[^:]: %[^\r\n]", w1, w2);
+ while (fgets(line, sizeof(line), fp)) {
+ i = sscanf(line, "%1023[^:]: %1023[^\r\n]", w1, w2);
if(i != 2)
continue;
@@ -742,16 +956,18 @@ static int inter_config_read(const char* cfgName)
return 0;
}
-// Save interlog into sql
-int inter_log(char* fmt, ...)
-{
+/**
+ * Save interlog into sql (arglist version)
+ * @see inter_log
+ */
+int inter_vlog(char* fmt, va_list ap) {
char str[255];
char esc_str[sizeof(str)*2+1];// escaped str
- va_list ap;
+ va_list apcopy;
- va_start(ap,fmt);
- vsnprintf(str, sizeof(str), fmt, ap);
- va_end(ap);
+ va_copy(apcopy, ap);
+ vsnprintf(str, sizeof(str), fmt, apcopy);
+ va_end(apcopy);
SQL->EscapeStringLen(sql_handle, esc_str, str, strnlen(str, sizeof(str)));
if( SQL_ERROR == SQL->Query(sql_handle, "INSERT INTO `%s` (`time`, `log`) VALUES (NOW(), '%s')", interlog_db, esc_str) )
@@ -760,6 +976,23 @@ int inter_log(char* fmt, ...)
return 0;
}
+/**
+ * Save interlog into sql
+ * @param fmt Message's format string
+ * @param ... Additional (printf-like) arguments
+ * @return Always 0 // FIXME
+ */
+int inter_log(char* fmt, ...) {
+ va_list ap;
+ int ret;
+
+ va_start(ap,fmt);
+ ret = inter_vlog(fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
// initialize
int inter_init_sql(const char *file)
{
@@ -790,12 +1023,11 @@ int inter_init_sql(const char *file)
inter_homunculus_sql_init();
inter_mercenary_sql_init();
inter_elemental_sql_init();
- inter_accreg_sql_init();
inter_mail_sql_init();
inter_auction_sql_init();
- geoip_readdb();
- msg_config_read("conf/messages.conf");
+ geoip_init();
+ msg_config_read("conf/messages.conf", false);
return 0;
}
@@ -814,8 +1046,7 @@ void inter_final(void)
inter_mail_sql_final();
inter_auction_sql_final();
- if (accreg_pt) aFree(accreg_pt);
-
+ geoip_final(true);
do_final_msg();
return;
}
@@ -829,7 +1060,7 @@ int inter_mapif_init(int fd)
//--------------------------------------------------------
// broadcast sending
-int mapif_broadcast(unsigned char *mes, int len, unsigned long fontColor, short fontType, short fontSize, short fontAlign, short fontY, int sfd)
+int mapif_broadcast(unsigned char *mes, int len, unsigned int fontColor, short fontType, short fontSize, short fontAlign, short fontY, int sfd)
{
unsigned char *buf = (unsigned char*)aMalloc((len)*sizeof(unsigned char));
@@ -878,36 +1109,15 @@ int mapif_wis_end(struct WisData *wd, int flag)
}
// Account registry transfer to map-server
-static void mapif_account_reg(int fd, unsigned char *src)
-{
- WBUFW(src,0)=0x3804; //NOTE: writing to RFIFO
- mapif_sendallwos(fd, src, WBUFW(src,2));
-}
+//static void mapif_account_reg(int fd, unsigned char *src)
+//{
+// WBUFW(src,0)=0x3804; //NOTE: writing to RFIFO
+// mapif_sendallwos(fd, src, WBUFW(src,2));
+//}
// Send the requested account_reg
-int mapif_account_reg_reply(int fd,int account_id,int char_id, int type)
-{
- struct accreg *reg=accreg_pt;
- WFIFOHEAD(fd, 13 + 5000);
- inter_accreg_fromsql(account_id,char_id,reg,type);
-
- WFIFOW(fd,0)=0x3804;
- WFIFOL(fd,4)=account_id;
- WFIFOL(fd,8)=char_id;
- WFIFOB(fd,12)=type;
- if(reg->reg_num==0){
- WFIFOW(fd,2)=13;
- }else{
- int i,p;
- for (p=13,i = 0; i < reg->reg_num && p < 5000; i++) {
- p+= sprintf((char*)WFIFOP(fd,p), "%s", reg->reg[i].str)+1; //We add 1 to consider the '\0' in place.
- p+= sprintf((char*)WFIFOP(fd,p), "%s", reg->reg[i].value)+1;
- }
- WFIFOW(fd,2)=p;
- if (p>= 5000)
- ShowWarning("Too many acc regs for %d:%d, not all values were loaded.\n", account_id, char_id);
- }
- WFIFOSET(fd,WFIFOW(fd,2));
+int mapif_account_reg_reply(int fd,int account_id,int char_id, int type) {
+ inter_accreg_fromsql(account_id,char_id,fd,type);
return 0;
}
@@ -934,9 +1144,9 @@ int mapif_disconnectplayer(int fd, int account_id, int char_id, int reason)
*/
int check_ttl_wisdata_sub(DBKey key, DBData *data, va_list ap)
{
- unsigned long tick;
+ int64 tick;
struct WisData *wd = DB->data2ptr(data);
- tick = va_arg(ap, unsigned long);
+ tick = va_arg(ap, int64);
if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX)
wis_dellist[wis_delnum++] = wd->id;
@@ -946,7 +1156,7 @@ int check_ttl_wisdata_sub(DBKey key, DBData *data, va_list ap)
int check_ttl_wisdata(void)
{
- unsigned long tick = iTimer->gettick();
+ int64 tick = timer->gettick();
int i;
do {
@@ -956,7 +1166,7 @@ int check_ttl_wisdata(void)
struct WisData *wd = (struct WisData*)idb_get(wis_db, wis_dellist[i]);
ShowWarning("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst);
// removed. not send information after a timeout. Just no answer for the player
- //mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ //mapif_wis_end(wd, 1); // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target
idb_remove(wis_db, wd->id);
}
} while(wis_delnum >= WISDELLIST_MAX);
@@ -990,7 +1200,7 @@ int mapif_parse_WisRequest(int fd)
if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) {
ShowWarning("inter: Wis message size too long.\n");
return 0;
- } else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows...
+ } else if (RFIFOW(fd,2)-52 <= 0) { // normally, impossible, but who knows...
ShowError("inter: Wis message doesn't exist.\n");
return 0;
}
@@ -1007,7 +1217,7 @@ int mapif_parse_WisRequest(int fd)
unsigned char buf[27];
WBUFW(buf, 0) = 0x3802;
memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
- WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ WBUFB(buf,26) = 1; // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target
mapif_send(fd, buf, 27);
}
else
@@ -1022,7 +1232,7 @@ int mapif_parse_WisRequest(int fd)
uint8 buf[27];
WBUFW(buf, 0) = 0x3802;
memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), NAME_LENGTH);
- WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ WBUFB(buf,26) = 1; // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target
mapif_send(fd, buf, 27);
}
else
@@ -1039,7 +1249,7 @@ int mapif_parse_WisRequest(int fd)
memcpy(wd->src, RFIFOP(fd, 4), NAME_LENGTH);
memcpy(wd->dst, RFIFOP(fd,28), NAME_LENGTH);
memcpy(wd->msg, RFIFOP(fd,52), wd->len);
- wd->tick = iTimer->gettick();
+ wd->tick = timer->gettick();
idb_put(wis_db, wd->id, wd);
mapif_wis_message(wd);
}
@@ -1063,7 +1273,7 @@ int mapif_parse_WisReply(int fd)
return 0; // This wisp was probably suppress before, because it was timeout of because of target was found on another map-server
if ((--wd->count) <= 0 || flag != 1) {
- mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
+ mapif_wis_end(wd, flag); // flag: 0: success to send whisper, 1: target character is not logged in?, 2: ignored by target
idb_remove(wis_db, id);
}
@@ -1085,34 +1295,50 @@ int mapif_parse_WisToGM(int fd)
// Save account_reg into sql (type=2)
int mapif_parse_Registry(int fd)
{
- int j,p,len, max;
- struct accreg *reg=accreg_pt;
-
- memset(accreg_pt,0,sizeof(struct accreg));
- switch (RFIFOB(fd, 12)) {
- case 3: //Character registry
- max = GLOBAL_REG_NUM;
- break;
- case 2: //Account Registry
- max = ACCOUNT_REG_NUM;
- break;
- case 1: //Account2 registry, must be sent over to login server.
- return save_accreg2(RFIFOP(fd,4), RFIFOW(fd,2)-4);
- default:
- return 1;
- }
- for(j=0,p=13;j<max && p<RFIFOW(fd,2);j++){
- sscanf((char*)RFIFOP(fd,p), "%31c%n",reg->reg[j].str,&len);
- reg->reg[j].str[len]='\0';
- p +=len+1; //+1 to skip the '\0' between strings.
- sscanf((char*)RFIFOP(fd,p), "%255c%n",reg->reg[j].value,&len);
- reg->reg[j].value[len]='\0';
- p +=len+1;
- }
- reg->reg_num=j;
+ int account_id = RFIFOL(fd, 4), char_id = RFIFOL(fd, 8), count = RFIFOW(fd, 12);
+
+ if( count ) {
+ int cursor = 14, i;
+ char key[32], sval[254];
+ unsigned int index;
+
+ global_accreg_to_login_start(account_id,char_id);
+
+ for(i = 0; i < count; i++) {
+ safestrncpy(key, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor));
+ cursor += RFIFOB(fd, cursor) + 1;
+
+ index = RFIFOL(fd, cursor);
+ cursor += 4;
+
+ switch (RFIFOB(fd, cursor++)) {
+ /* int */
+ case 0:
+ inter_savereg(account_id,char_id,key,index,RFIFOL(fd, cursor),false);
+ cursor += 4;
+ break;
+ case 1:
+ inter_savereg(account_id,char_id,key,index,0,false);
+ break;
+ /* str */
+ case 2:
+ safestrncpy(sval, (char*)RFIFOP(fd, cursor + 1), RFIFOB(fd, cursor));
+ cursor += RFIFOB(fd, cursor) + 1;
+ inter_savereg(account_id,char_id,key,index,(intptr_t)sval,true);
+ break;
+ case 3:
+ inter_savereg(account_id,char_id,key,index,0,true);
+ break;
+
+ default:
+ ShowError("mapif_parse_Registry: unknown type %d\n",RFIFOB(fd, cursor - 1));
+ return 1;
+ }
- inter_accreg_tosql(RFIFOL(fd,4),RFIFOL(fd,8),reg, RFIFOB(fd,12));
- mapif_account_reg(fd,RFIFOP(fd,0)); // Send updated accounts to other map servers.
+ }
+
+ global_accreg_to_login_send();
+ }
return 0;
}
@@ -1151,8 +1377,8 @@ int mapif_parse_NameChangeRequest(int fd)
type = RFIFOB(fd,10);
name = (char*)RFIFOP(fd,11);
- // Check Authorised letters/symbols in the name
- if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorised
+ // Check Authorized letters/symbols in the name
+ if (char_name_option == 1) { // only letters/symbols in char_name_letters are authorized
for (i = 0; i < NAME_LENGTH && name[i]; i++)
if (strchr(char_name_letters, name[i]) == NULL) {
mapif_namechange_ack(fd, account_id, char_id, type, 0, name);
@@ -1239,23 +1465,4 @@ int inter_parse_frommap(int fd)
return 1;
}
-uint64 inter_chk_lastuid(int8 flag, uint64 value){
- static uint64 last_updt_uid = 0;
- static int8 update = 0;
- if(flag)
- {
- if(last_updt_uid < value){
- last_updt_uid = value;
- update = 1;
- }
-
- return 0;
- }else if(update)
- {
- update = 0;
- return last_updt_uid;
- }
- return 0;
-}
-