Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 05/26/19 in all areas

  1. 10 points
  2. M2 Download Center Download Here ( Internal ) Download Here ( GitHub ) Before: After:
    4 points
  3. I suppose you use the system which i made 4 years ago (2015-2016), i published it, so it's fine. The problem is in root/introLoading.py. You added the self.__RegisterTitlePrestigeName() inside of the function def DEBUG_LoadData(self, playerX, playerY), need to be in def LoadData(self, playerX, playerY) too.
    2 points
  4. Hi guys, Running a server for a year and a half while a bunch of guys gets paid to destroy it by any means ends up giving you a good insight in what the word security really means. So, after being the target of almost every type of attack possible over the Internet, I think I am ready today to write a quick checklist on how your Metin2 server should be secured from criminal and disruptive behavior. I will divide it into several parts: ingame, website, server, staff. I will try to explain in brief how those attacks are executed, but the focus here is in how to defend from them effectively. I do not want this checklist to serve the purposes of hackers. But be aware; you are never 100% safe - it depends on how much your enemy is willing to work on it. So this should be taken as a bare minimum. And don't forget: the science of destruction evolves constantly. What is considered safe today, might not be so safe tomorrow. Disclaimer: This text reflects 5 years of experience managing a server's technical aspects. I do not do certain things (like quests) for a long time so excuse me for any mistakes I may be making here. Part 1 - Ingame bugs and exploits This part refers to threats that may realize through the game client or external software that acts through the game client. While often overlooked, sometimes these can be more dangerous to your server than any other type of attack. Keep an eye in your server's economy - the average price of items. If prices for a particular item or all of them change suddenly, there may be some obscure reason behind it. That is one of the reasons why we keep public and private statistics on the average market price of every item at WoM. One of the most common mistakes - and I commited this myself - when one wants to extensively edit everything is having an item give you more Yang when you NPC it than it costs to buy it from a shop. This gives unlimited Yang to whoever discovers it. There are even public bots which automate the process of buying and selling these items. The effect on the economy is devastating over a sufficient long period, not to mention that it's unfair that some people are infinitely rich and unbalance is never good for any MMORPG. Have your GM double check that no item can be sold for more than it costs. However, this is not the only way that a player can obtain access to unlimited resources. Badly written quests may allow players to obtain their rewards repeatedly by closing their clients while a quest dialog is open. One simple way to avoid this is to make sure that the commands which reward the player are the last in the code block before changing state. Now going into disruptive behaviour - the most annoying probably are the bugs which cause a server core to crash. The most famous is probably the number_ex bug. This causes several commands which depend on this function such as dice to be exploitable by using certain parameters. This bug is fixed in rev 40k and therefore in any game compiled from the source. If you are using 34k, the best you can do is use iMer's lib which provides several security enhancements. If you are using 2089, there are public diffs that patch this. Another bug exists in the war command in 34k and older which leads to a server crash. You cannot disable this command as it is used by the guild leaders to war other guilds. iMer's Lib takes care of this bug in 34k. Older and less known is the gold drop core crash bug present in 2089 and fixed -I believe- in 34k. Dropping huge amounts of gold or any other item which you can acquire in huge numbers in a map will cause the server to crash. I am not aware of public fixes for this exploit. Another favourite is the kickhack or sync position hack -partially fixed only in 40k- which allows a malicious user to disconnect other players at will. There are even videos of this being done in DE years ago. iMer's lib provides a fix for rev 34k. Finally, we have the long list of cheats which purely seek to gain advantage while playing. There are several client protection tutorials and services around this forum so I won't extend myself here about the subject. Part 2 - Server and OS Here we talk about the threats that affect your server and Operating System Part 2.1 Choosing and preparing your server There is no doubt at the moment of writing this that the french hosting company OVH provides the most cost-effective anti-ddos solution in the market. While many companies provide DDoS protection services, these cost several times as much as OVH offerings and this increase does not necessarily translate into a better protection. Therefore we will assume that you are renting either a dedicated server at OVH or a VPS at Eterhost as those are the ones I'm familiar with. Other providers or resellers may be configured in a different manner. Dedicated Server at OVH There are two flavors of the Anti-DDoS solution: Basic and Pro. Basic is free when renting a Kimsufi or Soyoustart server (cheap and intermediate OVH brands respectively). What it does is detect attacks directed at your IP address and route your traffic through the mitigation system during the attack. This is not enough to deter a dedicated hacker though - before the attack is detected, your server may be collapsed for a few minutes, and this can be repeated every hour by the attacker, as happened with one of the TEC attacks on WoM about 1 month ago. The PRO version comes with all the OVH proper (also known as professional) line of servers. It adds the ability to route traffic permanently through the mitigation system and provides you with a simple hardware firewall. This is what you should get if serious attacks are a concern, specifically an Enterprise line server (cheapest is at about 100€/month, VAT included) How to activate permanent mitigation on OVH Professional servers: - Open the new manager. If you don't know where this is, open the classic manager and click on "Home" and then below on the "Dedicated" icon. - Click on IP on the column at the left - Click the little wheel that appears at the right of your server's IP address - Select "Mitigation: permanent" How to set up the hardware firewall: - Click the wheel again and select "Activate firewall". Wait around a minute and then reload the page. - Click the wheel and select "Configure firewall" - You will be presented with the rules screen. You can add up to 20 rules to deny or allow specific ports on the firewall. Start by adding the allowed ports in the lower numbers and end with a global deny rule. Leaving a field blank in the "Add Rule" dialog will result in a wildcard; so if you leave the IP address field blank, this rule will affect all IPs. Adding an allow port port rule: click on Add Rule; choose rule priority; select protocol (TCP or UDP); select Accept; enter the port number. Adding a global deny: choose rule priority (must be higher than the allow port rules); select protocol (TCP or UDP); select Deny. Here is an example set of rules. We have added the SSH port and the game & auth ports, as well as 3306 so our website can connect to MySQL. You can specify the webserver's IP in the rule to prevent others from connecting to your database, although I personally prefer to do this at the software level with the pf firewall; in any case, port 3306 must not be public. "How do I connect with Navicat then?" Simple, use the SSH Tunnel option. This logs you in to SSH and then connects to the MySQL server as localhost. You must enter your server's SSH login details in the SSH, and your MySQL login details in the General tab, but remember we are connecting to localhost or 127.0.0.1: that's what you must enter in hostname, NOT your server's public address, as MySQL doesn't see us as a remote user anymore. Regarding UDP: UDP is your enemy. Disable it completely as I did in the above ruleset. The official client previously used UDP to check on server status, but it's trivial to override it in python so it always shows "NORM" regardless of the result of servercheck. If you are using 40k or source, the check already uses TCP, but your ports must be lower than 32768 (or the source modified) for it to work. VPS at Eterhost Our VPS are always routed through the mitigation system. Hardware firewall can be activated and configured to your needs for 5€/month extra. Part 2.2 - Hardening SSH First things first: have your OS always updated to the current version. It is often recommended by experts to access your server through a non-administrative account (some operating systems such as CentOS even force you to create one at install), so that's what we will be doing. We will create a restricted user for our game server: adduser game We can leave everything as default except for the login group: enter "wheel" here. Users which are part of the wheel group can use the su command to gain administrator privileges. This allows us to completely block root from remote login. Now it's time to create a SSH key for our new user. This makes bruteforce virtually impossible. Finally, let's prevent login from root, and login without key: ee /etc/ssh/sshd_config Look for the "PermitRootLogin" line and leave it this way, uncommenting if necessary: PermitRootLogin no And next look for the "PasswordAuthentication" setting and edit it so it looks like this: PasswordAuthentication no Now let's restart ssh for changes to take effect: service sshd restart Warning: at this point, you should open a new putty window to test that you can login with the new user and key, and that you can use the su command to gain root privileges. Wrong settings may lock you out of your server. Once everything is working, upload your server files to /home/game. This is the home directory of the user - a bit like "My Documents" in Windows. Make sure you change permissions in the files so the "game" user can read and write them: chown -R game /home/game (this command must be run as root! only root can change someone else's permissions. To switch between the game and root account, you use the su (short for superuser) command: root# su game Password: (game user's password) game# su Password: (root user's password) root# Or: root# su game Password: (game user's password) game# exit root# Finally, it may be a good idea to install ssh-guard for extra security or change your SSH port if you don't like your logs being spammed by portscans. To change your SSH port, edit the Port line in /etc/ssh/sshd_config and restart the sshd service as indicated above. Remember to open the new port if you are using a firewall or you will lock yourself out of the server. Part 2.3 Software Firewall: pf At this point it's a good idea to add a second line of defense with the pf firewall. First we will create the /etc/pf.conf file following this sample config. ee /etc/pf.conf Make sure you enter the correct interface and all the ports which must be open: SSH and game ports in our case. Neither p2p ports or db core port should be open to the public! Under trusted_hosts, enter the IP address of your web server so it can connect to the database (We assume a typical set up of web server + game / db server here) instead of opening port 3306 to everyone. This firewall provides us with scrubbing and rate limiting capabilities which the hardware firewall doesn't. Next we will enable pf: kldload pf pfctl -e Check that everything is working fine and then add the following line to /etc/rc.conf to load pf on boot: pf_enable="YES"
    1 point
  5. M2 Download Center Download Here ( Internal ) Hi devs, I created simple wait hack detection and ban system open "battle.cpp" and add ths include #include "db.h" Result; search "bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time)" and replace with this #define ENABLE_SYSCHAT_NOTICE #define ENABLE_SYSLOG_NOTICE #define ENABLE_BAN_WAITHACK bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) { if (ch->m_kAttackLog.dwVID == victim->GetVID()) { if (current_time - ch->m_kAttackLog.dwTime < GET_ATTACK_SPEED(ch)) { INCREASE_SPEED_HACK_COUNT(ch); if (test_server) { sys_log(0, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", ch->GetName(), current_time - ch->m_kAttackLog.dwTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); ch->ChatPacket(CHAT_TYPE_INFO, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", ch->GetName(), current_time - ch->m_kAttackLog.dwTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); } SET_ATTACK_TIME(ch, victim, current_time); SET_ATTACKED_TIME(ch, victim, current_time); return true; } } SET_ATTACK_TIME(ch, victim, current_time); if (victim->m_AttackedLog.dwPID == ch->GetPlayerID()) { if (current_time - victim->m_AttackedLog.dwAttackedTime < GET_ATTACK_SPEED(ch)) { INCREASE_SPEED_HACK_COUNT(ch); if (ch->m_speed_hack_count > 50) { #ifdef ENABLE_SYSLOG_NOTICE sys_log(0, "%s Attack Speed HACK! time (delta, limit)=(%u, %u), hack_count = %d", ch->GetName(), current_time - victim->m_AttackedLog.dwAttackedTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); #endif #ifdef ENABLE_SYSCHAT_NOTICE ch->ChatPacket(CHAT_TYPE_INFO, "Attack Speed Hack(%s), (delta, limit)=(%u, %u), hack_count = %d", ch->GetName(), current_time - victim->m_AttackedLog.dwAttackedTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); #endif #ifdef ENABLE_BAN_WAITHACK std::auto_ptr<SQLMsg> msg(DBManager::instance().DirectQuery("UPDATE account.account SET status= 'BLOCK' WHERE id = %d", ch->GetDesc()->GetAccountTable().id)); #endif ch->GetDesc()->DelayedDisconnect(3); } SET_ATTACKED_TIME(ch, victim, current_time); return true; } } SET_ATTACKED_TIME(ch, victim, current_time); return false; } Before: [Hidden Content] [Hidden Content] After: [Hidden Content] [Hidden Content] Final:
    1 point
  6. M2 Download Center Download Here ( Internal ) Hello, I started to convert some server data files from .txt to .json. I intend to convert them more understandable and modern with these changes, also few bugs and a memory leak in the old system has been fixed. Currently only mob_drop_info.txt file is translated, then all .txt files and proto files will be added. Tutorial for mob_drop_info.txt game part: Add to service.h: #define ENABLE_JSON_GAME_FILES Add to stl.h inline std::wstring StringToWstring(std::string input) { std::wstring output(input.begin(), input.end()); return output; } inline std::string WstringToString(std::wstring input) { std::string output(input.begin(), input.end()); return output; } Search in input_db.cpp "%s/mob_drop_item.txt", LocaleService_GetBasePath().c_str()); Change with: #ifdef ENABLE_JSON_GAME_FILES "%s/mob_drop_item.json", LocaleService_GetBasePath().c_str()); #else "%s/mob_drop_item.txt", LocaleService_GetBasePath().c_str()); #endif Search: if (!ITEM_MANAGER::instance().ReadMonsterDropItemGroup(szMOBDropItemFileName)) Change with: #ifdef ENABLE_JSON_GAME_FILES if (!ITEM_MANAGER::instance().ReadMonsterDropItemGroupNew(szMOBDropItemFileName)) #else if (!ITEM_MANAGER::instance().ReadMonsterDropItemGroup(szMOBDropItemFileName)) #endif Search in item_manager.h: bool ReadDropItemGroup(const char * c_pszFileName); Add it under: #ifdef ENABLE_JSON_GAME_FILES bool ReadMonsterDropItemGroupNew(const char * c_pszFileName); #endif Add in item_manager_read_tables.cpp #ifdef ENABLE_JSON_GAME_FILES #include <fstream> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> #endif Search: bool ITEM_MANAGER::ReadMonsterDropItemGroup(const char * c_pszFileName) Change like this: [Hidden Content] Codes: [Hidden Content] Converter: [Hidden Content] Note: You need c ++ 11 and boost property tree module to use this configuration.
    1 point
  7. I think you are looking for: Change 160 for your gauge
    1 point
  8. Boost PropertyTree is not a json library (but it uses internally one). It drops tons of functionality from the JSON format. I also think it treats integers as strings. It's 20 times slower than the most used json libraries. Its implementation looks very oldish. It's basically legacy code in its utmost definition. Also, with the new c++ standards, many boost functionality can be replaced quite easily. Boost remains a bloatware for small projects, so I try to avoid it when possible. As for other alternatives, these two are header-only (easily to implement/copy-paste on Extern/include/ or from "pkg install") and much more beautiful: [Hidden Content] [Hidden Content]
    1 point
  9. Today I'm going to explain how to add and modify the different sounds that exist in the game. First of all we have the soundtrack; the music that is played in each map or when we choose it from the music menu. This is pretty straightforward as we just have to copy mp3 files to the bgm folder and they will be read automatically. In order to add the mp3 file as default sound on a map, you would edit locale/whatever/settings.lua and find lines looking like this add_bgm_info(125, "chasing_dragon.mp3", 0.5) Where the first value is the map index, second the filename and third the volume. Pretty straightforward. The next option the game offers us in the accoustic department is adding a sound to a motion, such as a skill or an emotion. In order to do this, we just need to find the msa file which we want to add a sound to. We will go to the folder of the mob, npc or character in question, find the motion we want through the motlist (if it's not a character), and then; create the respective folder under sound if it doesn't exist. For example, for adding a sound to the motion npc/bakchon/01.msa we would make the folder sound/npc/backchon/ and in it a text file name 01.mss with this format: ScriptType CharacterSoundInformation SoundDataCount 2 SoundData00 0.396000 "sound/monster/ice_snow_golem/drop_1.wav" SoundData01 0.000000 "sound/monster/ice_snow_golem/fall_1.wav" SoundDataCount: number of sounds SoundData00 etc is just used to list the sounds in order and finally the number represents the time, in seconds, that the sound will wait to be played once the motion starts. Of course if the time is higher than the motion duration, the sound is never going to work. The third option for adding sounds is doing it in the msa files themselves through MOTION_EVENT_TYPE_SOUND. I'm not going to comment this because it's never used by YMir and the mss methos is just easier. Finally, we can add sounds to an specific place in a map, such as water running under a bridge. To do this, we have to create a .pra file in the property folder/pack with the following information: YPRT 778119171 ambiencesoundvector "sound/ambience/water_underbridge.wav" propertyname "water_underbridge" propertytype "Ambience" maxvolumeareapercentage "0.300000" playinterval "0.000000" playintervalvariation "0.000000" playtype "LOOP" YPRT: just an identifier so the game knows its a property file (Ymir PRoperTy) The number is supposed to be the CRC of the file, but you can use whatever you want as long as it's unique. If you change this number later, the asset will disappear from the maps where you used it so be careful. ambiencesoundvector: filename of a WAV or MP3 file propertyname: just a name to identify it, can be anything. propertytype: in this case Ambience and be careful with case, it's an uppercase A at the start! maxvolumeareapercentage: which percentage (0 to 1) of the radius around the placement of the sound is played at 100% volume. I would set this to 1 for non-looping sounds as they only play once when you enter the defined area and won't play again till you leave and re-enter it. playinterval: how frequently a sound of type STEP plays playintervalvariation: random variation on the previous interval. playtype: ONCE, STEP or LOOP Once in World Editor, we can add our sounds as any other object; except we will set their Size, or the radius around the placement where it will be played, with the AreaAmbienceSize control in WE. If maxvolumeareasize is less than 1, the sound will get quieter from that % of the radius and end up being totally silent at the edge of the total radius. As I mentioned, all the sounds in the game can be either MP3 or WAV. A comment about WAV files. Most WAV files we can find on the internet will not work with Metin2 straight away. We can use MP3 instead, but we are adding unnecessary overhead to the game, so if you want to use WAV files in Metin here's how. - Download and install the free Audacity Sample Editor. [Hidden Content] - Open the original WAV file with it. You may need to press CTRL+A to select the whole sample. - Metin2 doesn't play stereo WAVs so if your WAV is stereo head to the Track menu and choose > Mix > Mix stereo down to mono. - Choose Resample... in the same menu and then 22050 - In the top left select "Project Rate" and change it to 22050 - Finally go to the File menu and click on Export and then WAV. Choose 16 bit PCM as format and done.
    1 point
  10. Hello folks. I don't know if it was "a feature" or not, but it was really annoying and it caused some bugs. So, let's begin. Firstly, we should go to the char.cpp (and we will stay here) Now, let's find the ApplyPoint function and then certain cases.. case APPLY_MAX_HP: case APPLY_MAX_HP_PCT: { int i = GetMaxHP(); if(i == 0) break; PointChange(aApplyInfo[bApplyType].bPointType, iVal); float fRatio = (float)GetMaxHP() / (float)i; PointChange(POINT_HP, GetHP() * fRatio - GetHP()); } break; case APPLY_MAX_SP: case APPLY_MAX_SP_PCT: { int i = GetMaxSP(); if(i == 0) break; PointChange(aApplyInfo[bApplyType].bPointType, iVal); float fRatio = (float)GetMaxSP() / (float)i; PointChange(POINT_SP, GetSP() * fRatio - GetSP()); } break; This part of code is written by Nova, which is used in novaline, but i know that some of you use it in other branches. It is causing the main problem, we should change it to look like this: case APPLY_MAX_HP: case APPLY_MAX_HP_PCT: { int i = GetMaxHP(); if(i == 0) break; PointChange(aApplyInfo[bApplyType].bPointType, iVal); } break; case APPLY_MAX_SP: case APPLY_MAX_SP_PCT: { int i = GetMaxSP(); if(i == 0) break; PointChange(aApplyInfo[bApplyType].bPointType, iVal); } break; Done, now we should conern next problem, it will cause a disproportion between max_hp and current hp. Moving on, we should find PointChange function (still in char.cpp). Then, find case POINT_MAX_HP and POINT_MAX_SP, and change it like that: case POINT_MAX_HP: { SetPoint(type, GetPoint(type) + amount); int i = GetMaxHP(); int hp = GetRealPoint(POINT_MAX_HP); int add_hp = MIN(3500, hp * GetPoint(POINT_MAX_HP_PCT) / 100); add_hp += GetPoint(POINT_MAX_HP); add_hp += GetPoint(POINT_PARTY_TANKER_BONUS); SetMaxHP(hp + add_hp); float fRatio = (float)GetMaxHP() / (float)i; PointChange(POINT_HP, GetHP() * fRatio - GetHP()); val = GetMaxHP(); } break; case POINT_MAX_SP: { SetPoint(type, GetPoint(type) + amount); int i = GetMaxSP(); int sp = GetRealPoint(POINT_MAX_SP); int add_sp = MIN(800, sp * GetPoint(POINT_MAX_SP_PCT) / 100); add_sp += GetPoint(POINT_MAX_SP); add_sp += GetPoint(POINT_PARTY_SKILL_MASTER_BONUS); SetMaxSP(sp + add_sp); float fRatio = (float)GetMaxSP() / (float)i; PointChange(POINT_SP, GetSP() * fRatio - GetSP()); val = GetMaxSP(); } break; Short description: I'm using Nova method to calculate HP/SP ratio gain. It should prevent people from using it as a infinite source of hp (using affect.add(max_hp.. affect.add(hp... we would create another bug.) Kind regards, Evor. @DISCLAIMER This method was tested by me and my team. We find it fixing the whole problem, but whenever you find another bug, please report it. Ratio calculation courtesy of Nova.
    1 point
  11. #Respect to @Shogun and everyone who shares free security solutions for servers out there. Security is not a priviledge, it's something nessecary and sharing it is what makes us human beings... Thanks so much for this guide I appreciate it a lot
    1 point
  12. Metod log > more secure : #define ENABLE_SYSCHAT_NOTICE #define ENABLE_SYSLOG_NOTICE #define ENABLE_BAN_WAITHACK bool IS_SPEED_HACK(LPCHARACTER ch, LPCHARACTER victim, DWORD current_time) { if (ch->m_kAttackLog.dwVID == victim->GetVID()) { if (current_time - ch->m_kAttackLog.dwTime < GET_ATTACK_SPEED(ch)) { INCREASE_SPEED_HACK_COUNT(ch); if (test_server) { sys_log(0, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", ch->GetName(), current_time - ch->m_kAttackLog.dwTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); ch->ChatPacket(CHAT_TYPE_INFO, "%s attack hack! time (delta, limit)=(%u, %u) hack_count %d", ch->GetName(), current_time - ch->m_kAttackLog.dwTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); } SET_ATTACK_TIME(ch, victim, current_time); SET_ATTACKED_TIME(ch, victim, current_time); return true; } } SET_ATTACK_TIME(ch, victim, current_time); if (victim->m_AttackedLog.dwPID == ch->GetPlayerID()) { if (current_time - victim->m_AttackedLog.dwAttackedTime < GET_ATTACK_SPEED(ch)) { INCREASE_SPEED_HACK_COUNT(ch); if (ch->m_speed_hack_count > 100) { #ifdef ENABLE_SYSLOG_NOTICE sys_log(0, "%s Attack Speed HACK! time (delta, limit)=(%u, %u), hack_count = %d", ch->GetName(), current_time - victim->m_AttackedLog.dwAttackedTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); #endif #ifdef ENABLE_SYSCHAT_NOTICE ch->ChatPacket(CHAT_TYPE_INFO, "A hack was detected , close it!", ch->GetName(), current_time - victim->m_AttackedLog.dwAttackedTime, GET_ATTACK_SPEED(ch), ch->m_speed_hack_count); #endif #ifdef ENABLE_BAN_WAITHACK DBManager::instance().Query("INSERT INTO log.wait_hack SET login='%s', nickname='%s', ip='%s', time=NOW(), map_index=%d, server='%s';", ch->GetDesc()->GetAccountTable().login, ch->GetName(), ch->GetDesc()->GetHostName(), ch->GetMapIndex(), g_stHostname.c_str()); #endif ch->GetDesc()->DelayedDisconnect(3); } SET_ATTACKED_TIME(ch, victim, current_time); return true; } } SET_ATTACKED_TIME(ch, victim, current_time); return false; } Create in database log a table with name : wait_hack /* Navicat MySQL Data Transfer Source Server : test Source Server Version : 50613 Source Host : Source Database : log Target Server Type : MYSQL Target Server Version : 50613 File Encoding : 65001 Date: 2015-01-02 12:54:57 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for wait_hack -- ---------------------------- DROP TABLE IF EXISTS `wait_hack`; CREATE TABLE `wait_hack` ( `login` varchar(50) COLLATE big5_bin DEFAULT NULL, `nickname` varchar(50) COLLATE big5_bin DEFAULT NULL, `ip` varchar(50) COLLATE big5_bin DEFAULT NULL, `time` time DEFAULT NULL, `map_index` varchar(30) COLLATE big5_bin DEFAULT NULL, `server` varchar(30) COLLATE big5_bin DEFAULT NULL, `playtime` int(11) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=big5 COLLATE=big5_bin; It is more secure now .
    1 point
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.