• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

character manager

Gubailovo

Well-Known Member
Joined
Dec 19, 2013
Messages
407
Solutions
2
Reaction score
62
I have a character manager in the client. It creates characters on the account and saves it to the database. But with a log out, he rolls it back.121332.gif
Lua:
NewCharacter = { }

local protocolCreate
local loadBox

-- consts
SEX_MALE           = 0
SEX_FEMALE         = 1


-- Filter Settings
local filters = {
  sex      = SEX_MALE
}

-- private functions
local function onError(protocol, message, errorCode)
  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end

  if not errorCode then
    NewCharacter.clearCharNameField()
  end

  local errorBox = displayErrorBox(tr('Create Error'), message)
  connect(errorBox, { onOk = NewCharacter.show })
end

local function onCharacterList(protocol, characters, account, otui)
  loadBox:destroy()
  loadBox = nil

  for _, characterInfo in pairs(characters) do
    if characterInfo.previewState and characterInfo.previewState ~= PreviewState.Default then
      characterInfo.worldName = characterInfo.worldName .. ', Preview'
    end
  end

  CharacterList.create(characters, account, otui)
  CharacterList.show()
end

function NewCharacter.clearCharNameField()
  newCharacterWin:getChildById('characterNameTextEdit'):clearText()
  newCharacterWin:getChildById('characterNameTextEdit'):focus()
end

-- public functions
function NewCharacter.init()
  newCharacterWin = g_ui.displayUI('newcharacterwin')
 
  sexBoxMale         = newCharacterWin:getChildById('sexBoxMale')
  sexBoxFemale         = newCharacterWin:getChildById('sexBoxFemale')
 
  sexRadioGroup = UIRadioGroup.create()
  sexRadioGroup:addWidget(sexBoxMale)
  sexRadioGroup:addWidget(sexBoxFemale)
 
  sexRadioGroup:selectWidget(sexBoxMale)
 
  sexRadioGroup.onSelectionChange   = toggleFilter

  --if g_app.isRunning() and g_game.isOnline() then
   -- newCharacterWin:show()
  --end

end

function toggleFilter(widget, selectedWidget)
  if widget == sexRadioGroup then
    local boxId = selectedWidget:getId()
    if boxId == 'sexBoxMale' then
      filters.sex = SEX_MALE
    elseif boxId == 'sexBoxFemale' then
      filters.sex = SEX_FEMALE
    end
  end

end

function NewCharacter.show()
  if loadBox or errorBox or not newCharacterWin then return end
  CharacterList.hide(false)
  newCharacterWin:show()
  newCharacterWin:raise()
  newCharacterWin:focus()
end

function NewCharacter.hide()
  newCharacterWin:hide()

  if CharacterList and not g_game.isOnline() then
    CharacterList.show()
  end
end

function NewCharacter.terminate()
  newCharacterWin:destroy()
  newCharacterWin = nil
 
  NewCharacter = nil
end

function NewCharacter.doCreateChar()
  newCharacterWin:hide()
  local characterName = newCharacterWin:getChildById('characterNameTextEdit'):getText()
  local isMan = newCharacterWin:getChildById('sexBoxMale'):isChecked()
  local characterSex = 0
 
  if isMan then
    characterSex = 1
  end
 

  protocolCreate = ProtocolLogin.create()
  protocolCreate.onLoginError = onError
  protocolCreate.onCharacterList = onCharacterList
  
  loadBox = displayCancelBox(tr('Please wait'), tr('Create a character...'))
  connect(loadBox, { onCancel = function(msgbox)
                                  loadBox = nil
                                  protocolCreate:cancelLogin()
                                  NewCharacter.show()
                                end })

  if modules.game_things.isLoaded() then
    protocolCreate:createChar(G.host, G.port, G.account, G.password, characterName, characterSex)
  else
    loadBox:destroy()
    loadBox = nil
    NewCharacter.show()
  end
end
 
It probably creates the character in the database with the save column set to 0 instead of 1.
can you tell me how to fix it?
Post automatically merged:

it creates a character but does not save its data
 
Last edited:
I've created same thing before for a guy using otcv8, by using lua to send json to PHP.
it may help you with the query.
(all the name checks i took from an aac which i do not recall the name)

PHP:
<?php
// initializing variables
$maxCharsPerAccount = 10;
$server_name = "Myserver name";
$names_blocked = array('admin', 'administrator', 'gm', 'cm', 'god', 'tutor');
$words_blocked = array('admin', 'administrator', 'gamemaster', 'game master', 'game-master', "game'master", '--', "''","' ", " '", '- ', ' -', "-'", "'-", 'fuck', 'sux', 'suck', 'noob', 'tutor');
$first_words_blocked = array('admin ', 'administrator ', 'gm ', 'cm ', 'god ','tutor ', "'", '-');
//
$errors = array();

// connect to the database
$db = mysqli_connect('localhost', 'root', '', 'test');
// Check connection
if($db === false){
    die("ERROR: Could not connect. " . mysqli_connect_error());
}

// Takes raw data from the request
$json = file_get_contents('php://input');

// Converts it into a PHP object
$data = json_decode($json);
 
// REGISTER USER

// receive all input values from the form
$charactername = $data->name;
$account = $data->account;
$sex = $data->sex;
$faction = $data->faction;

// form validation: ensure that the form is correctly filled ...
// by adding (array_push()) corresponding error unto $errors array
if (empty($charactername)) { array_push($errors, "Character name is required"); }
if (empty($account)) { array_push($errors, "account is required"); }
if (empty($sex)) { array_push($errors, "sex is required"); }
if (empty($faction)) { array_push($errors, "faction is required"); }
if (strlen($charactername) > 25) { array_push($errors, "Name is too long. Max. lenght 25 letters."); }
if (strlen($charactername) < 3) { array_push($errors, "Name is too short. Min. lenght 3 letters."); }

$length = strlen($charactername);
if($length < 3) { array_push($errors, "Name is too short. Min. lenght 3 letters."); }
if($length > 25) { array_push($errors, "Name is too long. Max. lenght 25 letters."); }
if(strspn($charactername, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- [ ] '") != $length)
{
    array_push($errors, "Invalid name format. Use only A-Z, spaces and '.");
}
if(!preg_match("/[A-z ']/", $charactername))
{
    array_push($errors, "Invalid name format. Use only A-Z, spaces and '.");
}

// get player id
$findaccid_query = "SELECT * FROM accounts WHERE name='$account' LIMIT 1";
$findaccid_query_result = mysqli_query($db, $findaccid_query);
$acc = mysqli_fetch_assoc($findaccid_query_result);
if ($acc) { // if user exists
    $account_id = $acc['id'];
} else {
    array_push($errors, "Account id not found");
} 

$findaccchars_query = "select COUNT(*) AS count FROM test.players WHERE account_id='$account_id'";
$findaccchars_query_result = mysqli_query($db, $findaccchars_query);
// make sure it worked
if(!$findaccchars_query_result) {
    die('Error: not found query');
} else {
    $num_rows = mysqli_fetch_assoc($findaccchars_query_result);
    $totalcount = $num_rows['count'];
    if ($totalcount > $maxCharsPerAccount) {
        array_push($errors, "Too many characters in your account.");
    }
}

$name_lower = strtolower($charactername);
foreach($first_words_blocked as $word)
{
    if($word == substr($name_lower, 0, strlen($word))) {
        array_push($errors, "Your name contains blocked words.");
    }
}

if(substr($name_lower, -1) == "'" || substr($name_lower, -1) == "-") {
    
    array_push($errors, "Your name contains illegal characters.");
}

if(substr($name_lower, 1, 1) == ' ') {
    array_push($errors, "Your name contains illegal space.");
}

if(substr($name_lower, -2, 1) == " ") {
    array_push($errors, "Your name contains illegal space.");
}

if(strtolower($server_name) == $name_lower) {
    array_push($errors, "Your name cannot be same as server name.");
}

foreach($names_blocked as $word)
{
    if($word == $name_lower) {
        array_push($errors, "Your name contains blocked words.");
    }
}

foreach($words_blocked as $word)
{
    if(!(strpos($name_lower, $word) === false)) {
        array_push($errors, "Your name contains illegal words.");
    }
}
$invalid_name = false;
$name_length = strlen($name_lower);
for($i = 0; $i < $name_length; $i++)
{
    if(isset($name_lower[$i]) && isset($name_lower[$i + 1]) && $name_lower[$i] == $name_lower[$i + 1] && isset($name_lower[$i + 2]) && $name_lower[$i] == $name_lower[$i + 2]) {
        $invalid_name = true;
    }
}


for($i = 0; $i < $name_length; $i++)
{
    if(isset($name_lower[$i - 1]) && $name_lower[$i - 1] == ' ' && isset($name_lower[$i + 1]) && $name_lower[$i + 1] == ' ') {
        $invalid_name = true;
    }
}

if ($invalid_name == true) {
    array_push($errors, "Your name is invalid.");     
}
// first check the database to make sure
// a user does not already exist with the same name
$findplayer_query = "SELECT * FROM players WHERE name='$charactername' LIMIT 1";
$findplayer_result = mysqli_query($db, $findplayer_query);
$player = mysqli_fetch_assoc($findplayer_result);

if ($player) { // if user exists
    if ($player['name'] === $charactername) {
      array_push($errors, "Name in use");
    }
}

// Finally, register user if there are no errors in the form
if (count($errors) == 0) { 
    $query = "INSERT INTO `players`(`name`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `save`, `skull`, `skulltime`, `lastlogout`, `blessings`, `direction`) VALUES ('".$charactername."', '1', '".$account_id."', '8', '".$faction."', '185', '185', '4200', '68', '76', '78', '58', '128', '0', '0', '40', '40', '0', '100', '1', '5', '5', '2', '', '470', '1', '0', '0', '1', '0', '0', '0', '0', '0')";
    mysqli_query($db, $query);

    // prepare items and skills
    $findnewplayerid_query = "SELECT * FROM players WHERE name='$charactername' LIMIT 1";
    $findnewplayerid_query_result = mysqli_query($db, $findnewplayerid_query);
    $newplayer = mysqli_fetch_assoc($findnewplayerid_query_result);
    if ($newplayer) { // if user exists
        $player_id = $newplayer['id'];
        echo "Character " . $charactername . " Created.";
    } else {
        array_push($errors, "account not created, player id not found");
    }     
}   
?>

<?php  if (count($errors) > 0) : ?>
        <?php echo "error : " . $errors[0] ?>
<?php  endif ?>
 
I've created same thing before for a guy using otcv8, by using lua to send json to PHP.
it may help you with the query.
(all the name checks i took from an aac which i do not recall the name)

PHP:
<?php
// initializing variables
$maxCharsPerAccount = 10;
$server_name = "Myserver name";
$names_blocked = array('admin', 'administrator', 'gm', 'cm', 'god', 'tutor');
$words_blocked = array('admin', 'administrator', 'gamemaster', 'game master', 'game-master', "game'master", '--', "''","' ", " '", '- ', ' -', "-'", "'-", 'fuck', 'sux', 'suck', 'noob', 'tutor');
$first_words_blocked = array('admin ', 'administrator ', 'gm ', 'cm ', 'god ','tutor ', "'", '-');
//
$errors = array();

// connect to the database
$db = mysqli_connect('localhost', 'root', '', 'test');
// Check connection
if($db === false){
    die("ERROR: Could not connect. " . mysqli_connect_error());
}

// Takes raw data from the request
$json = file_get_contents('php://input');

// Converts it into a PHP object
$data = json_decode($json);

// REGISTER USER

// receive all input values from the form
$charactername = $data->name;
$account = $data->account;
$sex = $data->sex;
$faction = $data->faction;

// form validation: ensure that the form is correctly filled ...
// by adding (array_push()) corresponding error unto $errors array
if (empty($charactername)) { array_push($errors, "Character name is required"); }
if (empty($account)) { array_push($errors, "account is required"); }
if (empty($sex)) { array_push($errors, "sex is required"); }
if (empty($faction)) { array_push($errors, "faction is required"); }
if (strlen($charactername) > 25) { array_push($errors, "Name is too long. Max. lenght 25 letters."); }
if (strlen($charactername) < 3) { array_push($errors, "Name is too short. Min. lenght 3 letters."); }

$length = strlen($charactername);
if($length < 3) { array_push($errors, "Name is too short. Min. lenght 3 letters."); }
if($length > 25) { array_push($errors, "Name is too long. Max. lenght 25 letters."); }
if(strspn($charactername, "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM- [ ] '") != $length)
{
    array_push($errors, "Invalid name format. Use only A-Z, spaces and '.");
}
if(!preg_match("/[A-z ']/", $charactername))
{
    array_push($errors, "Invalid name format. Use only A-Z, spaces and '.");
}

// get player id
$findaccid_query = "SELECT * FROM accounts WHERE name='$account' LIMIT 1";
$findaccid_query_result = mysqli_query($db, $findaccid_query);
$acc = mysqli_fetch_assoc($findaccid_query_result);
if ($acc) { // if user exists
    $account_id = $acc['id'];
} else {
    array_push($errors, "Account id not found");
}

$findaccchars_query = "select COUNT(*) AS count FROM test.players WHERE account_id='$account_id'";
$findaccchars_query_result = mysqli_query($db, $findaccchars_query);
// make sure it worked
if(!$findaccchars_query_result) {
    die('Error: not found query');
} else {
    $num_rows = mysqli_fetch_assoc($findaccchars_query_result);
    $totalcount = $num_rows['count'];
    if ($totalcount > $maxCharsPerAccount) {
        array_push($errors, "Too many characters in your account.");
    }
}

$name_lower = strtolower($charactername);
foreach($first_words_blocked as $word)
{
    if($word == substr($name_lower, 0, strlen($word))) {
        array_push($errors, "Your name contains blocked words.");
    }
}

if(substr($name_lower, -1) == "'" || substr($name_lower, -1) == "-") {
   
    array_push($errors, "Your name contains illegal characters.");
}

if(substr($name_lower, 1, 1) == ' ') {
    array_push($errors, "Your name contains illegal space.");
}

if(substr($name_lower, -2, 1) == " ") {
    array_push($errors, "Your name contains illegal space.");
}

if(strtolower($server_name) == $name_lower) {
    array_push($errors, "Your name cannot be same as server name.");
}

foreach($names_blocked as $word)
{
    if($word == $name_lower) {
        array_push($errors, "Your name contains blocked words.");
    }
}

foreach($words_blocked as $word)
{
    if(!(strpos($name_lower, $word) === false)) {
        array_push($errors, "Your name contains illegal words.");
    }
}
$invalid_name = false;
$name_length = strlen($name_lower);
for($i = 0; $i < $name_length; $i++)
{
    if(isset($name_lower[$i]) && isset($name_lower[$i + 1]) && $name_lower[$i] == $name_lower[$i + 1] && isset($name_lower[$i + 2]) && $name_lower[$i] == $name_lower[$i + 2]) {
        $invalid_name = true;
    }
}


for($i = 0; $i < $name_length; $i++)
{
    if(isset($name_lower[$i - 1]) && $name_lower[$i - 1] == ' ' && isset($name_lower[$i + 1]) && $name_lower[$i + 1] == ' ') {
        $invalid_name = true;
    }
}

if ($invalid_name == true) {
    array_push($errors, "Your name is invalid.");    
}
// first check the database to make sure
// a user does not already exist with the same name
$findplayer_query = "SELECT * FROM players WHERE name='$charactername' LIMIT 1";
$findplayer_result = mysqli_query($db, $findplayer_query);
$player = mysqli_fetch_assoc($findplayer_result);

if ($player) { // if user exists
    if ($player['name'] === $charactername) {
      array_push($errors, "Name in use");
    }
}

// Finally, register user if there are no errors in the form
if (count($errors) == 0) {
    $query = "INSERT INTO `players`(`name`, `group_id`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `maglevel`, `mana`, `manamax`, `manaspent`, `soul`, `town_id`, `posx`, `posy`, `posz`, `conditions`, `cap`, `sex`, `lastlogin`, `lastip`, `save`, `skull`, `skulltime`, `lastlogout`, `blessings`, `direction`) VALUES ('".$charactername."', '1', '".$account_id."', '8', '".$faction."', '185', '185', '4200', '68', '76', '78', '58', '128', '0', '0', '40', '40', '0', '100', '1', '5', '5', '2', '', '470', '1', '0', '0', '1', '0', '0', '0', '0', '0')";
    mysqli_query($db, $query);

    // prepare items and skills
    $findnewplayerid_query = "SELECT * FROM players WHERE name='$charactername' LIMIT 1";
    $findnewplayerid_query_result = mysqli_query($db, $findnewplayerid_query);
    $newplayer = mysqli_fetch_assoc($findnewplayerid_query_result);
    if ($newplayer) { // if user exists
        $player_id = $newplayer['id'];
        echo "Character " . $charactername . " Created.";
    } else {
        array_push($errors, "account not created, player id not found");
    }    
}  
?>

<?php  if (count($errors) > 0) : ?>
        <?php echo "error : " . $errors[0] ?>
<?php  endif ?>

thanks

but there is no character creation window for this script?
(otui)
 
I cannot send to you because it was a paid job :( If i had permission id post all my codes. sadly i was stupid to sell codes that i wish everyone had access to...

btw, there you have a query that sends the info to database, so you can check whats wrong in your query. but as said before, its probably the "save" that needs to be 1.
 
I cannot send to you because it was a paid job :( If i had permission id post all my codes. sadly i was stupid to sell codes that i wish everyone had access to...

btw, there you have a query that sends the info to database, so you can check whats wrong in your query. but as said before, its probably the "save" that needs to be 1.
he doesn't give an id to the character
33333.jpg
Post automatically merged:

It probably creates the character in the database with the save column set to 0 instead of 1.
the reason is not this and not the id (the database simply does not synchronize the progress of the characters)
 
Last edited:
i see, i really must see your query in order to help you. as you can see the query i used there's no need to define the player id.

like @Alpha said, post here the createChar function and we will take it from there

edit, it may have something to do with your tfs version. the query i post is made for tfs 1.2 and i see your database structure is different.
db.png
 
Last edited:
i see, i really must see your query in order to help you. as you can see the query i used there's no need to define the player id.

like @Alpha said, post here the createChar function and we will take it from there

edit, it may have something to do with your tfs version. the query i post is made for tfs 1.2 and i see your database structure is different.
View attachment 52098
I never found a solution to the problem. (what is required of me in order for you to help me?)
Post automatically merged:

Lua:
rotocolCreate = {}

local protocol

local function send(msg)
  if protocol then
    protocol:send(msg)
  end
end

function initProtocol()
  ProtocolCreate.registerProtocol()
end

function terminateProtocol()
  ProtocolCreate.unregisterProtocol()
  ProtocolCreate = nil
end

local function parseCharCreateError(protocol, msg)
  local errorMessage = msg:getString()
  signalcall(self.onLoginError, self, errorMessage)

  return true
end

function ProtocolCreate.updateProtocol(_protocol)
  protocol = _protocol
end

function ProtocolCreate.registerProtocol()
  ProtocolGame.registerOpcode(GameServerOpcodes.GameServerCharCreateError, parseCharCreateError)
  ProtocolCreate.updateProtocol(g_game.getProtocolGame())
  g_logger.info(g_game.getProtocolGame())
end

function ProtocolCreate.unregisterProtocol()
  ProtocolGame.unregisterOpcode(GameServerOpcodes.GameServerCharCreateError, parseCharCreateError)
  ProtocolCreate.updateProtocol(nil)
end

function ProtocolCreate.createChar(host, port, accName, accPassword, charName, charSex)
  if string.len(host) == 0 or port == nil or port == 0 then
    signalcall(self.onLoginError, self, tr("You must enter a valid server address and port."))
    return
  end

  local msg = OutputMessage.create()
  -- ID протокола
  msg:addU16(0x0201)

  -- пустые 15 байт
  msg:addU8(0xFF)
  msg:addU16(0xFFFF)
  msg:addU32(0xFFFFFFFF)
  msg:addU32(0xFFFFFFFF)
  msg:addU32(0xFFFFFFFF)

  -- логин и пароль
  msg:addU32(tonumber(accName))
  msg:addString(accPassword)
  msg:addU8(0x20)

  --имя и пол нового персонажа
  msg:addString(charName)
  msg:addU8(0)
  send(msg)
  --self:connect(host, port)
end

--function ProtocolCreate:cancelCreate()
--  self:disconnect()
--end


Lua:
NewCharacter = { }

local protocolCreate
local loadBox

-- consts
SEX_MALE           = 0
SEX_FEMALE         = 1


-- Filter Settings
local filters = {
  sex      = SEX_MALE
}

-- private functions
local function onError(protocol, message, errorCode)
  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end

  if not errorCode then
    NewCharacter.clearCharNameField()
  end

  local errorBox = displayErrorBox(tr('Create Error'), message)
  connect(errorBox, { onOk = NewCharacter.show })
end

local function onCharacterList(protocol, characters, account, otui)
  loadBox:destroy()
  loadBox = nil

  for _, characterInfo in pairs(characters) do
    if characterInfo.previewState and characterInfo.previewState ~= PreviewState.Default then
      characterInfo.worldName = characterInfo.worldName .. ', Preview'
    end
  end

  CharacterList.create(characters, account, otui)
  CharacterList.show()
end

function NewCharacter.clearCharNameField()
  newCharacterWin:getChildById('characterNameTextEdit'):clearText()
  newCharacterWin:getChildById('characterNameTextEdit'):focus()
end

-- public functions
function NewCharacter.init()
  newCharacterWin = g_ui.displayUI('newcharacterwin')

  sexBoxMale         = newCharacterWin:getChildById('sexBoxMale')
  sexBoxFemale         = newCharacterWin:getChildById('sexBoxFemale')

  sexRadioGroup = UIRadioGroup.create()
  sexRadioGroup:addWidget(sexBoxMale)
  sexRadioGroup:addWidget(sexBoxFemale)

  sexRadioGroup:selectWidget(sexBoxMale)

  sexRadioGroup.onSelectionChange   = toggleFilter

  --if g_app.isRunning() and g_game.isOnline() then
   -- newCharacterWin:show()
  --end

end

function toggleFilter(widget, selectedWidget)
  if widget == sexRadioGroup then
    local boxId = selectedWidget:getId()
    if boxId == 'sexBoxMale' then
      filters.sex = SEX_MALE
    elseif boxId == 'sexBoxFemale' then
      filters.sex = SEX_FEMALE
    end
  end

end

function NewCharacter.show()
  if loadBox or errorBox or not newCharacterWin then return end
  CharacterList.hide(false)
  newCharacterWin:show()
  newCharacterWin:raise()
  newCharacterWin:focus()
end

function NewCharacter.hide()
  newCharacterWin:hide()

  if CharacterList and not g_game.isOnline() then
    CharacterList.show()
  end
end

function NewCharacter.terminate()
  newCharacterWin:destroy()
  newCharacterWin = nil

  NewCharacter = nil
end

function NewCharacter.doCreateChar()
  newCharacterWin:hide()
  local characterName = newCharacterWin:getChildById('characterNameTextEdit'):getText()
  local isMan = newCharacterWin:getChildById('sexBoxMale'):isChecked()
  local characterSex = 0

  if isMan then
    characterSex = 1
  end


  protocolCreate = ProtocolLogin.create()
  protocolCreate.onLoginError = onError
  protocolCreate.onCharacterList = onCharacterList
 
  loadBox = displayCancelBox(tr('Please wait'), tr('Create a character...'))
  connect(loadBox, { onCancel = function(msgbox)
                                  loadBox = nil
                                  protocolCreate:cancelLogin()
                                  NewCharacter.show()
                                end })

  if modules.game_things.isLoaded() then
    protocolCreate:createChar(G.host, G.port, G.account, G.password, characterName, characterSex)
  else
    loadBox:destroy()
    loadBox = nil
    NewCharacter.show()
  end
end

Lua:
EnterGame = { }

-- private variables
local loadBox
local enterGame
local motdWindow
local motdButton
local enterGameButton
local clientBox
local protocolLogin
local motdEnabled = true

-- private functions
local function onError(protocol, message, errorCode)
  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end

  if not errorCode then
    EnterGame.clearAccountFields()
  end

  local errorBox = displayErrorBox(tr('Login Error'), message)
  connect(errorBox, { onOk = EnterGame.show })
end

local function onMotd(protocol, motd)
  G.motdNumber = tonumber(motd:sub(0, motd:find("\n")))
  G.motdMessage = motd:sub(motd:find("\n") + 1, #motd)
  if motdEnabled then
    motdButton:show()
  end
end

local function onSessionKey(protocol, sessionKey)
  G.sessionKey = sessionKey
end

local function onCharacterList(protocol, characters, account, otui)
  -- Try add server to the server list
  ServerList.add(G.host, G.port, g_game.getClientVersion())

  -- Save 'Stay logged in' setting
  g_settings.set('staylogged', enterGame:getChildById('stayLoggedBox'):isChecked())

  if enterGame:getChildById('rememberPasswordBox'):isChecked() then
    local account = g_crypt.encrypt(G.account)
    local password = g_crypt.encrypt(G.password)

    g_settings.set('account', account)
    g_settings.set('password', password)

    ServerList.setServerAccount(G.host, account)
    ServerList.setServerPassword(G.host, password)

    g_settings.set('autologin', enterGame:getChildById('autoLoginBox'):isChecked())
  else
    -- reset server list account/password
    ServerList.setServerAccount(G.host, '')
    ServerList.setServerPassword(G.host, '')

    EnterGame.clearAccountFields()
  end

  loadBox:destroy()
  loadBox = nil

  for _, characterInfo in pairs(characters) do
    if characterInfo.previewState and characterInfo.previewState ~= PreviewState.Default then
      characterInfo.worldName = characterInfo.worldName .. ', Preview'
    end
  end

  CharacterList.create(characters, account, otui)
  CharacterList.show()

  if motdEnabled then
    local lastMotdNumber = g_settings.getNumber("motd")
    if G.motdNumber and G.motdNumber ~= lastMotdNumber then
      g_settings.set("motd", G.motdNumber)
      motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage)
      connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end })
      CharacterList.hide()
    end
  end
end

local function onUpdateNeeded(protocol, signature)
  loadBox:destroy()
  loadBox = nil

  if EnterGame.updateFunc then
    local continueFunc = EnterGame.show
    local cancelFunc = EnterGame.show
    EnterGame.updateFunc(signature, continueFunc, cancelFunc)
  else
    local errorBox = displayErrorBox(tr('Update needed'), tr('Your client needs updating, try redownloading it.'))
    connect(errorBox, { onOk = EnterGame.show })
  end
end

-- public functions
function EnterGame.init()
  enterGame = g_ui.displayUI('entergame')
  enterGameButton = modules.client_topmenu.addLeftButton('enterGameButton', tr('Login') .. ' (Ctrl + G)', '/images/topbuttons/login', EnterGame.openWindow)
  motdButton = modules.client_topmenu.addLeftButton('motdButton', tr('Message of the day'), '/images/topbuttons/motd', EnterGame.displayMotd)
  motdButton:hide()
  g_keyboard.bindKeyDown('Ctrl+G', EnterGame.openWindow)

  if motdEnabled and G.motdNumber then
    motdButton:show()
  end

  local account = g_settings.get('account')
  local password = g_settings.get('password')
  local host = g_settings.get('host')
  local port = g_settings.get('port')
  local stayLogged = g_settings.getBoolean('staylogged')
  local autologin = g_settings.getBoolean('autologin')
  local clientVersion = g_settings.getInteger('client-version')
  if clientVersion == 0 then clientVersion = 1074 end

  if port == nil or port == 0 then port = 7171 end

  EnterGame.setAccountName(account)
  EnterGame.setPassword(password)

  enterGame:getChildById('serverHostTextEdit'):setText(host)
  enterGame:getChildById('serverPortTextEdit'):setText(port)
  enterGame:getChildById('autoLoginBox'):setChecked(autologin)
  enterGame:getChildById('stayLoggedBox'):setChecked(stayLogged)

  clientBox = enterGame:getChildById('clientComboBox')
  for _, proto in pairs(g_game.getSupportedClients()) do
    clientBox:addOption(proto)
  end
  clientBox:setCurrentOption(clientVersion)

  EnterGame.toggleAuthenticatorToken(clientVersion, true)
  EnterGame.toggleStayLoggedBox(clientVersion, true)
  connect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })

  enterGame:hide()

  if g_app.isRunning() and not g_game.isOnline() then
    enterGame:show()
  end
end

function EnterGame.firstShow()
  EnterGame.show()

  local account = g_crypt.decrypt(g_settings.get('account'))
  local password = g_crypt.decrypt(g_settings.get('password'))
  local host = g_settings.get('host')
  local autologin = g_settings.getBoolean('autologin')
  if #host > 0 and #password > 0 and #account > 0 and autologin then
    addEvent(function()
      if not g_settings.getBoolean('autologin') then return end
      EnterGame.doLogin()
    end)
  end
end

function EnterGame.terminate()
  g_keyboard.unbindKeyDown('Ctrl+G')
  disconnect(clientBox, { onOptionChange = EnterGame.onClientVersionChange })
  enterGame:destroy()
  enterGame = nil
  enterGameButton:destroy()
  enterGameButton = nil
  clientBox = nil
  if motdWindow then
    motdWindow:destroy()
    motdWindow = nil
  end
  if motdButton then
    motdButton:destroy()
    motdButton = nil
  end
  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end
  if protocolLogin then
    protocolLogin:cancelLogin()
    protocolLogin = nil
  end
  EnterGame = nil
end

function EnterGame.show()
  if loadBox then return end
  enterGame:show()
  enterGame:raise()
  enterGame:focus()
end

function EnterGame.hide()
  enterGame:hide()
end

function EnterGame.openWindow()
  if g_game.isOnline() then
    CharacterList.show()
  elseif not g_game.isLogging() and not CharacterList.isVisible() then
    EnterGame.show()
  end
end

function EnterGame.setAccountName(account)
  local account = g_crypt.decrypt(account)
  enterGame:getChildById('accountNameTextEdit'):setText(account)
  enterGame:getChildById('accountNameTextEdit'):setCursorPos(-1)
  enterGame:getChildById('rememberPasswordBox'):setChecked(#account > 0)
end

function EnterGame.setPassword(password)
  local password = g_crypt.decrypt(password)
  enterGame:getChildById('accountPasswordTextEdit'):setText(password)
end

function EnterGame.clearAccountFields()
  enterGame:getChildById('accountNameTextEdit'):clearText()
  enterGame:getChildById('accountPasswordTextEdit'):clearText()
  enterGame:getChildById('authenticatorTokenTextEdit'):clearText()
  enterGame:getChildById('accountNameTextEdit'):focus()
  g_settings.remove('account')
  g_settings.remove('password')
end

function EnterGame.toggleAuthenticatorToken(clientVersion, init)
  local enabled = (clientVersion >= 1072)
  if enabled == enterGame.authenticatorEnabled then
    return
  end

  enterGame:getChildById('authenticatorTokenLabel'):setOn(enabled)
  enterGame:getChildById('authenticatorTokenTextEdit'):setOn(enabled)

  local newHeight = enterGame:getHeight()
  local newY = enterGame:getY()
  if enabled then
    newY = newY - enterGame.authenticatorHeight
    newHeight = newHeight + enterGame.authenticatorHeight
  else
    newY = newY + enterGame.authenticatorHeight
    newHeight = newHeight - enterGame.authenticatorHeight
  end

  if not init then
    enterGame:breakAnchors()
    enterGame:setY(newY)
    enterGame:bindRectToParent()
  end
  enterGame:setHeight(newHeight)

  enterGame.authenticatorEnabled = enabled
end

function EnterGame.toggleStayLoggedBox(clientVersion, init)
  local enabled = (clientVersion >= 1074)
  if enabled == enterGame.stayLoggedBoxEnabled then
    return
  end

  enterGame:getChildById('stayLoggedBox'):setOn(enabled)

  local newHeight = enterGame:getHeight()
  local newY = enterGame:getY()
  if enabled then
    newY = newY - enterGame.stayLoggedBoxHeight
    newHeight = newHeight + enterGame.stayLoggedBoxHeight
  else
    newY = newY + enterGame.stayLoggedBoxHeight
    newHeight = newHeight - enterGame.stayLoggedBoxHeight
  end

  if not init then
    enterGame:breakAnchors()
    enterGame:setY(newY)
    enterGame:bindRectToParent()
  end
  enterGame:setHeight(newHeight)

  enterGame.stayLoggedBoxEnabled = enabled
end

function EnterGame.onClientVersionChange(comboBox, text, data)
  local clientVersion = tonumber(text)
  EnterGame.toggleAuthenticatorToken(clientVersion)
  EnterGame.toggleStayLoggedBox(clientVersion)
end

function EnterGame.doLogin()
  G.account = enterGame:getChildById('accountNameTextEdit'):getText()
  G.password = enterGame:getChildById('accountPasswordTextEdit'):getText()
  G.authenticatorToken = enterGame:getChildById('authenticatorTokenTextEdit'):getText()
  G.stayLogged = enterGame:getChildById('stayLoggedBox'):isChecked()
  G.host = enterGame:getChildById('serverHostTextEdit'):getText()
  G.port = tonumber(enterGame:getChildById('serverPortTextEdit'):getText())
  local clientVersion = tonumber(clientBox:getText())
  EnterGame.hide()

  if g_game.isOnline() then
    local errorBox = displayErrorBox(tr('Login Error'), tr('Cannot login while already in game.'))
    connect(errorBox, { onOk = EnterGame.show })
    return
  end

  g_settings.set('host', G.host)
  g_settings.set('port', G.port)
  g_settings.set('client-version', clientVersion)

  protocolLogin = ProtocolLogin.create()
  protocolLogin.onLoginError = onError
  protocolLogin.onMotd = onMotd
  protocolLogin.onSessionKey = onSessionKey
  protocolLogin.onCharacterList = onCharacterList
  protocolLogin.onUpdateNeeded = onUpdateNeeded

  loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to login server...'))
  connect(loadBox, { onCancel = function(msgbox)
                                  loadBox = nil
                                  protocolLogin:cancelLogin()
                                  EnterGame.show()
                                end })

  g_game.setClientVersion(clientVersion)
  g_game.setProtocolVersion(g_game.getClientProtocolVersion(clientVersion))
  g_game.chooseRsa(G.host)

  if modules.game_things.isLoaded() then
    protocolLogin:login(G.host, G.port, G.account, G.password, G.authenticatorToken, G.stayLogged)
  else
    loadBox:destroy()
    loadBox = nil
    EnterGame.show()
  end
end

function EnterGame.displayMotd()
  if not motdWindow then
    motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage)
    motdWindow.onOk = function() motdWindow = nil end
  end
end

function EnterGame.setDefaultServer(host, port, protocol)
  local hostTextEdit = enterGame:getChildById('serverHostTextEdit')
  local portTextEdit = enterGame:getChildById('serverPortTextEdit')
  local clientLabel = enterGame:getChildById('clientLabel')
  local accountTextEdit = enterGame:getChildById('accountNameTextEdit')
  local passwordTextEdit = enterGame:getChildById('accountPasswordTextEdit')
  local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')

  if hostTextEdit:getText() ~= host then
    hostTextEdit:setText(host)
    portTextEdit:setText(port)
    clientBox:setCurrentOption(protocol)
    accountTextEdit:setText('')
    passwordTextEdit:setText('')
    authenticatorTokenTextEdit:setText('')
  end
end

function EnterGame.setUniqueServer(host, port, protocol, windowWidth, windowHeight)
  local hostTextEdit = enterGame:getChildById('serverHostTextEdit')
  hostTextEdit:setText(host)
  hostTextEdit:setVisible(false)
  hostTextEdit:setHeight(0)
  local portTextEdit = enterGame:getChildById('serverPortTextEdit')
  portTextEdit:setText(port)
  portTextEdit:setVisible(false)
  portTextEdit:setHeight(0)

  local authenticatorTokenTextEdit = enterGame:getChildById('authenticatorTokenTextEdit')
  authenticatorTokenTextEdit:setText('')
  authenticatorTokenTextEdit:setOn(false)
  local authenticatorTokenLabel = enterGame:getChildById('authenticatorTokenLabel')
  authenticatorTokenLabel:setOn(false)

  local stayLoggedBox = enterGame:getChildById('stayLoggedBox')
  stayLoggedBox:setChecked(false)
  stayLoggedBox:setOn(false)

  clientBox:setCurrentOption(protocol)
  clientBox:setVisible(false)
  clientBox:setHeight(0)

  local serverLabel = enterGame:getChildById('serverLabel')
  serverLabel:setVisible(false)
  serverLabel:setHeight(0)
  local portLabel = enterGame:getChildById('portLabel')
  portLabel:setVisible(false)
  portLabel:setHeight(0)
  local clientLabel = enterGame:getChildById('clientLabel')
  clientLabel:setVisible(false)
  clientLabel:setHeight(0)

  local serverListButton = enterGame:getChildById('serverListButton')
  serverListButton:setVisible(false)
  serverListButton:setHeight(0)
  serverListButton:setWidth(0)

  local rememberPasswordBox = enterGame:getChildById('rememberPasswordBox')
  rememberPasswordBox:setMarginTop(-8)

  if not windowWidth then windowWidth = 236 end
  enterGame:setWidth(windowWidth)
  if not windowHeight then windowHeight = 210 end
  enterGame:setHeight(windowHeight)
end

function EnterGame.setServerInfo(message)
  local label = enterGame:getChildById('serverInfoLabel')
  label:setText(message)
end

function EnterGame.disableMotd()
  motdEnabled = false
  motdButton:hide()
end
 
Last edited:
We need to see this code

Lua:
protocolCreate:createChar(G.host, G.port, G.account, G.password, characterName, characterSex)
 
Lua:
CharacterList = { }

-- private variables
local charactersWindow
local loadBox
local characterList
local errorBox
local waitingWindow
local updateWaitEvent
local resendWaitEvent
local loginEvent

-- private functions
local function tryLogin(charInfo, tries)
  tries = tries or 1

  if tries > 50 then
    return
  end

  if g_game.isOnline() then
    if tries == 1 then
      g_game.safeLogout()
    end
    loginEvent = scheduleEvent(function() tryLogin(charInfo, tries+1) end, 100)
    return
  end

  CharacterList.hide()

  g_game.loginWorld(G.account, G.password, charInfo.worldName, charInfo.worldHost, charInfo.worldPort, charInfo.characterName, G.authenticatorToken, G.sessionKey)

  loadBox = displayCancelBox(tr('Please wait'), tr('Connecting to game server...'))
  connect(loadBox, { onCancel = function()
                                  loadBox = nil
                                  g_game.cancelLogin()
                                  CharacterList.show()
                                end })

  -- save last used character
  g_settings.set('last-used-character', charInfo.characterName)
  g_settings.set('last-used-world', charInfo.worldName)
end

local function updateWait(timeStart, timeEnd)
  if waitingWindow then
    local time = g_clock.seconds()
    if time <= timeEnd then
      local percent = ((time - timeStart) / (timeEnd - timeStart)) * 100
      local timeStr = string.format("%.0f", timeEnd - time)

      local progressBar = waitingWindow:getChildById('progressBar')
      progressBar:setPercent(percent)

      local label = waitingWindow:getChildById('timeLabel')
      label:setText(tr('Trying to reconnect in %s seconds.', timeStr))

      updateWaitEvent = scheduleEvent(function() updateWait(timeStart, timeEnd) end, 1000 * progressBar:getPercentPixels() / 100 * (timeEnd - timeStart))
      return true
    end
  end

  if updateWaitEvent then
    updateWaitEvent:cancel()
    updateWaitEvent = nil
  end
end

local function resendWait()
  if waitingWindow then
    waitingWindow:destroy()
    waitingWindow = nil

    if updateWaitEvent then
      updateWaitEvent:cancel()
      updateWaitEvent = nil
    end

    if charactersWindow then
      local selected = characterList:getFocusedChild()
      if selected then
        local charInfo = { worldHost = selected.worldHost,
                           worldPort = selected.worldPort,
                           worldName = selected.worldName,
                           characterName = selected.characterName }
        tryLogin(charInfo)
      end
    end
  end
end

local function onLoginWait(message, time)
  CharacterList.destroyLoadBox()

  waitingWindow = g_ui.displayUI('waitinglist')

  local label = waitingWindow:getChildById('infoLabel')
  label:setText(message)

  updateWaitEvent = scheduleEvent(function() updateWait(g_clock.seconds(), g_clock.seconds() + time) end, 0)
  resendWaitEvent = scheduleEvent(resendWait, time * 1000)
end

function onGameLoginError(message)
  CharacterList.destroyLoadBox()
  errorBox = displayErrorBox(tr("Login Error"), message)
  errorBox.onOk = function()
    errorBox = nil
    CharacterList.showAgain()
  end
end

function onGameLoginToken(unknown)
  CharacterList.destroyLoadBox()
  -- TODO: make it possible to enter a new token here / prompt token
  errorBox = displayErrorBox(tr("Two-Factor Authentification"), 'A new authentification token is required.\nPlease login again.')
  errorBox.onOk = function()
    errorBox = nil
    EnterGame.show()
  end
end

function onGameConnectionError(message, code)
  CharacterList.destroyLoadBox()
  local text = translateNetworkError(code, g_game.getProtocolGame() and g_game.getProtocolGame():isConnecting(), message)
  errorBox = displayErrorBox(tr("Connection Error"), text)
  errorBox.onOk = function()
    errorBox = nil
    CharacterList.showAgain()
  end
end

function onGameUpdateNeeded(signature)
  CharacterList.destroyLoadBox()
  errorBox = displayErrorBox(tr("Update needed"), tr('Enter with your account again to update your client.'))
  errorBox.onOk = function()
    errorBox = nil
    CharacterList.showAgain()
  end
end

-- public functions
function CharacterList.init()
  connect(g_game, { onLoginError = onGameLoginError })
  connect(g_game, { onLoginToken = onGameLoginToken })
  connect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
  connect(g_game, { onConnectionError = onGameConnectionError })
  connect(g_game, { onGameStart = CharacterList.destroyLoadBox })
  connect(g_game, { onLoginWait = onLoginWait })
  connect(g_game, { onGameEnd = CharacterList.showAgain })

  if G.characters then
    CharacterList.create(G.characters, G.characterAccount)
  end
end

function CharacterList.terminate()
  disconnect(g_game, { onLoginError = onGameLoginError })
  disconnect(g_game, { onLoginToken = onGameLoginToken })
  disconnect(g_game, { onUpdateNeeded = onGameUpdateNeeded })
  disconnect(g_game, { onConnectionError = onGameConnectionError })
  disconnect(g_game, { onGameStart = CharacterList.destroyLoadBox })
  disconnect(g_game, { onLoginWait = onLoginWait })
  disconnect(g_game, { onGameEnd = CharacterList.showAgain })

  if charactersWindow then
    characterList = nil
    charactersWindow:destroy()
    charactersWindow = nil
  end

  if loadBox then
    g_game.cancelLogin()
    loadBox:destroy()
    loadBox = nil
  end

  if waitingWindow then
    waitingWindow:destroy()
    waitingWindow = nil
  end

  if updateWaitEvent then
    removeEvent(updateWaitEvent)
    updateWaitEvent = nil
  end

  if resendWaitEvent then
    removeEvent(resendWaitEvent)
    resendWaitEvent = nil
  end

  if loginEvent then
    removeEvent(loginEvent)
    loginEvent = nil
  end

  CharacterList = nil
end

function CharacterList.create(characters, account, otui)
  if not otui then otui = 'characterlist' end

  if charactersWindow then
    charactersWindow:destroy()
  end

  charactersWindow = g_ui.displayUI(otui)
  characterList = charactersWindow:getChildById('characters')

  -- characters
  G.characters = characters
  G.characterAccount = account

  characterList:destroyChildren()
  local accountStatusLabel = charactersWindow:getChildById('accountStatusLabel')

  local focusLabel
  for i,characterInfo in ipairs(characters) do
    local widget = g_ui.createWidget('CharacterWidget', characterList)
    for key,value in pairs(characterInfo) do
      local subWidget = widget:getChildById(key)
      if subWidget then
        if key == 'outfit' then -- it's an exception
          subWidget:setOutfit(value)
        else
          local text = value
          if subWidget.baseText and subWidget.baseTranslate then
            text = tr(subWidget.baseText, text)
          elseif subWidget.baseText then
            text = string.format(subWidget.baseText, text)
          end
          subWidget:setText(text)
        end
      end
    end

    -- these are used by login
    widget.characterName = characterInfo.name
    widget.worldName = characterInfo.worldName
    widget.worldHost = characterInfo.worldIp
    widget.worldPort = characterInfo.worldPort

    connect(widget, { onDoubleClick = function () CharacterList.doLogin() return true end } )

    if i == 1 or (g_settings.get('last-used-character') == widget.characterName and g_settings.get('last-used-world') == widget.worldName) then
      focusLabel = widget
    end
  end

  if focusLabel then
    characterList:focusChild(focusLabel, KeyboardFocusReason)
    addEvent(function() characterList:ensureChildVisible(focusLabel) end)
  end

  -- account
  if account.premDays > 0 and account.premDays < 4000 then
    accountStatusLabel:setText(tr("Premium Account (%s) days left", account.premDays))
  elseif account.premDays >= 4000 then
    accountStatusLabel:setText(tr("Lifetime Premium Account"))
  else
    accountStatusLabel:setText(tr('Free Account'))
  end

  if account.premDays > 0 and account.premDays <= 7 then
    accountStatusLabel:setOn(true)
  else
    accountStatusLabel:setOn(false)
  end
end

function CharacterList.destroy()
  CharacterList.hide(true)

  if charactersWindow then
    characterList = nil
    charactersWindow:destroy()
    charactersWindow = nil
  end
end

function CharacterList.show()
  if loadBox or errorBox or not charactersWindow then return end
  charactersWindow:show()
  charactersWindow:raise()
  charactersWindow:focus()
end

function CharacterList.hide(showLogin)
  showLogin = showLogin or false
  charactersWindow:hide()

  if showLogin and EnterGame and not g_game.isOnline() then
    EnterGame.show()
  end
end

function CharacterList.showAgain()
  if characterList and characterList:hasChildren() then
    CharacterList.show()
  end
end

function CharacterList.isVisible()
  if charactersWindow and charactersWindow:isVisible() then
    return true
  end
  return false
end

function CharacterList.doLogin()
  local selected = characterList:getFocusedChild()
  if selected then
    local charInfo = { worldHost = selected.worldHost,
                       worldPort = selected.worldPort,
                       worldName = selected.worldName,
                       characterName = selected.characterName }
    charactersWindow:hide()
    if loginEvent then
      removeEvent(loginEvent)
      loginEvent = nil
    end
    tryLogin(charInfo)
  else
    displayErrorBox(tr('Error'), tr('You must select a character to login!'))
  end
end

function CharacterList.destroyLoadBox()
  if loadBox then
    loadBox:destroy()
    loadBox = nil
  end
end

function CharacterList.cancelWait()
  if waitingWindow then
    waitingWindow:destroy()
    waitingWindow = nil
  end

  if updateWaitEvent then
    removeEvent(updateWaitEvent)
    updateWaitEvent = nil
  end

  if resendWaitEvent then
    removeEvent(resendWaitEvent)
    resendWaitEvent = nil
  end

  CharacterList.destroyLoadBox()
  CharacterList.showAgain()
end
Post automatically merged:

where can i find it?
Post automatically merged:

We need to see this code

Lua:
protocolCreate:createChar(G.host, G.port, G.account, G.password, characterName, characterSex)
it just doesn't assign id to the character
 
Last edited:
this is because the client saves the character list in memory, and does not update it when you log out, so basically to update, you would need to redo the protocol send request character info every time the player logs out of the character...

when you enter the password, the client requests the information from the char's to the server and saves it in the memory so as not to be resending the same request several times
 
this is because the client saves the character list in memory, and does not update it when you log out, so basically to update, you would need to redo the protocol send request character info every time the player logs out of the character...

when you enter the password, the client requests the information from the char's to the server and saves it in the memory so as not to be resending the same request several times
how do i fix this?
 
this is because the client saves the character list in memory, and does not update it when you log out, so basically to update, you would need to redo the protocol send request character info every time the player logs out of the character...

when you enter the password, the client requests the information from the char's to the server and saves it in the memory so as not to be resending the same request several times
Client knows nothing about character (before login) other than it's name, world, host address and game world port. You are not saving character position or anything. As long as he sees his new character on the list, it's fine, he can login. What's wrong here is that whenever he inserts new player into database, id is not incremented, which is weird since players table has AUTO_INCREMENT enabled by default.

I can't post entire code for my Character Creator but server side code to insert character is as simple as this. Just INSERT with proper values and you good to go, check your players table and make sure AUTO_INCREMENT is enabled.
C++:
bool IOLoginData::createCharacter(const std::string& accountName, const std::string& characterName, uint16_t sex,
    uint16_t lookType, uint16_t lookHead, uint16_t lookBody, uint16_t lookLegs, uint16_t lookFeet, uint16_t vocation, uint16_t town, uint32_t ip)
{
    Database& db = Database::getInstance();

    std::ostringstream query;
    query << "SELECT `id` FROM `accounts` WHERE `name` = " << db.escapeString(accountName);
    DBResult_ptr result = Database::getInstance().storeQuery(query.str());
    if (!result) {
        return false;
    }

    auto accountId = result->getNumber<uint32_t>("id");
    Vocation* voc = g_vocations.getVocation(vocation);
    uint16_t level = 8;
    uint16_t experience = 4200;
    uint16_t health = voc->getBaseHealth();
    uint16_t mana = voc->getBaseMana();
    uint16_t cap = voc->getBaseCap();
    uint16_t soul = voc->getBaseSoul();

    query.str(std::string());
    query << "INSERT INTO `players` ";
    query << "(`name`, `account_id`, `level`, `vocation`, `health`, `healthmax`, `experience`,";
    query << "`lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`,";
    query << "`mana`, `manamax`, `soul`, `cap`, `sex`, `lastip`, `conditions`, `town_id`) VALUES (";
    query << db.escapeString(characterName) << ',' << accountId << ',' << level << ',' << vocation << ',' << health << ',' << health << ',' << experience << ',';
    query << lookBody << ',' << lookFeet << ',' << lookHead << ',' << lookLegs << ',' << lookType << ',' << mana << ',' << mana << ',' << soul << ',';
    query << cap << ',' << sex << ',' << ip << ',' << '0' << ',' << town << ')';
    return db.executeQuery(query.str());
}
 
Last edited:
Back
Top