Conan Exiles Dedicated Server Launcher (Official Version: 1.7.11 / Beta: 1.7.12)

Sorry for the late answer, I’ve been kind of busy recently and the DSL has been on the back burner.

The fact that there is a 1 minute delay is kind of strange.

The way the log is updated is indeed like a tail: I track the file size, and when it changes I load the new content from the last position I had loaded.

Then I search for tokens that match a specific content, in this case the code is like that:

                

std::string_view joinSucceededToken("Join succeeded:");
std::string_view playerDisconnecctedToken("Player disconnected:");
(...)
// Player filtering
m_ServerLogFilePlayersContent.append(line);
m_ServerLogFilePlayersContent.append("\r\n");
(...)
auto pos = line.find(joinSucceededToken);
if (pos != line.npos)
{
  // [2022.10.20-08.56.56:349][418]LogNet: Join succeeded: name#id
  pos += joinSucceededToken.size();
  dw::String playerName = line.substr(pos);
  m_CurrentPlayers.insert(playerName.Strip());
}
else
{
  pos = line.find(playerDisconnecctedToken);
  if (pos != line.npos)
  {
    // [2022.10.20-09.39.28:612][590]LogNet: Player disconnected: name#id
    pos += playerDisconnecctedToken.size();
    dw::String playerName = line.substr(pos);
    m_CurrentPlayers.erase(playerName.Strip());
  }
}

then at the end of the parsing, without any delay, the player tab is modified if the number of players changed.

int playerCount = (int)m_CurrentPlayers.size();
if (m_CurrentPlayerCount != playerCount)
{
  m_CurrentPlayerCount = playerCount;

  // If the number of players has changed, we modify the tab to indicate the number of players
  dw::String playersTabCaption = "Players";
  if (m_CurrentPlayerCount)
  {
    playersTabCaption.FormatAppend(" (%u)", m_CurrentPlayerCount);
  }
  SetTabCaption(IDC_TABBED_VIEW, (int)TabEntry::e_TabEntryPlayers, playersTabCaption);
}

The m_ServerLogFilePlayersContent is used to populate the end of the player tab, and as you can see is processed at the same time as the data is extracted from the log.

Here is 100% of the code that generates the player’s tab:

    case TabEntry::e_TabEntryPlayers:
      {
        dw::String logContent;
        if (m_CurrentPlayers.empty())
        {
          logContent.FormatAppend("No player connected\r\n");
        }
        else
        {
          logContent.FormatAppend("Players currently connected\r\n");
          for (const auto& playerName : m_CurrentPlayers)
          {
            logContent.FormatAppend("- %s\r\n",playerName);
          }
        }
        logContent.FormatAppend("-=-=-=-=-\r\n");
        logContent.FormatAppend("%s",m_ServerLogFilePlayersContent);

        SetItemText(IDC_LOG_FILE_SNIPPET, logContent);
      }
      break;

This is called at the end of the log parsing, and all it does is to print the list of all the players in the m_CurrentPlayers which was updated above, then the -=-= separator, and finally the m_ServerLogFilePlayersContent content mentioned earlier.

Out of the box, I’ve no idea how they could get out of sync, obviously the UI has some delays to avoid slowing down things, but when you click on a tab, the refresh is instant and based on up-to-date content.

@tiagoturilli regarding the

but I must warn you that every time a new version is released, the DSL becomes heavier to run.

I don’t believe that’s true at all, most of the changes I’ve done to the DSL are like a couple lines here and there, the main change is that I often need to publish a new DSL when a major change was done in the game server.

If what you are saying is actually true, then I need some numbers that show that, like show me your task manager running various versions of the DSL using significantly different amount of memory and CPU usage.

1 Like

A power outage at the work site today resulted in an unexpected opportunity to look into current status of my report.

tiagoturilli wrote:

“I think you are configuring something wrong or you don’t know how to configure it properly, I have two servers and it works very well without any problems so far…”

The upper section of DSL has check boxes, edit boxes and drop downs for settings - which can I set wrong to cause such a problem? Your answer will help me fix my problem, and will save toolguy work effort not having to look through his code for a bug. It will also help him help others like me who mess up a setting that he apparently does not know will affect the QOL tab in the lower section (I am assuming he doesn’t know: he asked me if I had done something but did not indicate a setting issue). Looking at my server computer’s history, it appears I have been using DSL since version 1.6.2. There have never been any issues with the servers related to DSL. The only problem I have had is the server computer sometimes crashed - corrected when I resized the swap file.

tiagoturilli wrote:

“I hope this is not a problem that generates another DSL because there are no problems with the current DSL but I must warn that every time a new version is released the DSL becomes heavier to execute, I hope you have a good machine like I have to run your Conan Exiles servers.”

Never paid attention to how much additional load DSL caused with each update but then I never had any issues beyond swap file size. Given I have an unexpected time slot and how easy DSL makes it to set up new servers, I decided to look at various verions’ loads. Due to the game server name change, it appears I can only go back to 1.7.1 to compare, but that is a 9 version span. Versions 1.7.1, 1.7.5 and 1.7.7 were selected for comparison to the current 1.7.9 as well as each other.

Here is a screen shot of the task manager.

The three 1.7.9 DSL instances and their servers are 24/7 programs I run.

The steady state loads are identical. As I watched them, the server with active user(s) bounced up to a whopping 1.1% once in a while, more often around 0.3% - I suspect processing log file changes but no way to know for certain.

I would have tried to run all nine versions at once but I was afraid my potatoe computer would become mashed potatoes at which point the active players would see that I also had a mashed gourd.

The day I worry about the additional load updates to DSL places on my computer is the day I say to myself “Self, you are overdue replacing your potatoe computer.”

1 Like

No need to apologize - with the release of Age of Heroes I suspect you have had plenty to do. Been busy myself. As I said, this is a minor issue.

toolguy wrote:

“The fact that there is a 1 minute delay is kind of strange.”

Thought that too, not nailed it down but it looks like this is when players are out or AFK and no activity is occuring. The only lines added to the tab are “status” lines with the “players=” entry when there is a minute between updates.

Will look at the rest of your post and respond later.

— later :smiley:

If multiple lines in the log file can be written between the times your program gets the change, I see two conditions that will produce the results I am getting. It will be very rare occurrences, but gives me a condition to seek in my logs (no time tonight, and might be a few days).

First, the code checks to see if a join occurred. If a player joins and a second player’s join is in the same read from file, only the first join is detected in your buffer.

Second, the code tests for a join and if found does not check if a disconnect occurred in the same batch of lines read from the file. The code that checks for disconnects will miss a second disconnect if it is in the same batch.

My thought for a fix might cause some servers to be overloaded: remove the if/else and test for both. If join is found, check the string following that location for another join. Same for the disconnect.

1 Like

Since you seem to know programming, let’s dig with a bit more context :slight_smile:

So first, let’s see how the log loading/restart detection works:

void DedicatedServerLauncher::RefreshLogOutput(bool forced)
{
  // Log file
  bool needRefresh = forced;

  uint64 logFileSize = dw::IOFile::GetSize(m_ServerLogFileName);
  if (logFileSize != m_ServerLogFileSize)
  {
    if (logFileSize < m_ServerLogFileSize)
    {
      DW_Print(INFOL_IMPORTANT, e_DedicatedServerLauncher, "Log file %s size reduced from %d to %d bytes", m_ServerLogFileName, m_ServerLogFileSize, logFileSize);  // ---------------------------- DEBUGGING
      // If the log file is shorter than the later we loaded, it's probably a new one
      m_ServerLogFileContent.clear();
      (...)
      m_ServerLogFileParsedPosition = 0;
      m_CurrentPlayerCount = 0;
      m_CurrentPlayers.clear();
      SetTabCaption(IDC_TABBED_VIEW, (int)TabEntry::e_TabEntryPlayers, "Players");
    }
    m_ServerLogFileSize = logFileSize;

    dw::IOFile fileReader;
    if (fileReader.Open(m_ServerLogFileName, IOFILEFLAG_Read | IOFILEFLAG_FullPath))
    {
      uint64 currentSize = m_ServerLogFileContent.size();
      uint64 sizeToLoad = logFileSize - currentSize;
      if ((sizeToLoad > 0) && (sizeToLoad < 0xFFFFFFFF))
      {
        if (fileReader.SetPos(currentSize, IOPOS_Start))
        {
          // Load the newly added content from the game server log file
          void* data = fileReader.IOBase::Read((uint32)sizeToLoad);
          if (data)
          {

The “forced” parameter is generally set to false, when set to true it will force refreshing, that’s typically used when clicking on tabs to select a new one.

As you can see the detection is based on the filesize:

  • If it’s different it means the content has changed
  • If it’s smaller it means the server was restarted

If the new file is smaller, I flush all the internal parsed information and start loading from the start of the file.

If the file is larger, I seek to the last position I reached before and load the new payload.

At that point, data and sizeToLoad contain information about what was newly loaded

The code then enters a small set-up section:

// If it properly loaded, we add the content to an internal buffer
m_ServerLogFileContent.append(static_cast<char*>(data), sizeToLoad);

// These are patterns we can use to search for specific things in the log
std::string_view lineSeparators("\r\n");
std::string_view errorToken(":Error:");
(...)
std::string_view joinSucceededToken("Join succeeded:");
std::string_view playerDisconnecctedToken("Player disconnected:");
(...)
std::string_view logModManagerToken("LogModManager:");

// The search starts from the last successful position (that's in the case we got some partial lines loaded)
std::size_t lastParsedPosition = m_ServerLogFileParsedPosition;
std::string_view fileView(m_ServerLogFileContent.data() + lastParsedPosition, sizeToLoad);

Basically the “data” is appended to the current internal server log file content, then a bunch of string patterns are initialized (they are used to check the log file content later), and finally a “view” is created on the new content.

And then comes the parsing of the new content, which basically iterate on views delimited by \r\n markers:

// Using the \r\n pattern, we iterate over the file, line by line
std::size_t startPos = 0;
std::size_t endPos = 0;
while ( (endPos=fileView.find_first_of(lineSeparators, startPos)) != fileView.npos)
{
  // We managed to find an end of line separator, remember that as the last successfully parsed location
  lastParsedPosition = m_ServerLogFileParsedPosition + endPos + lineSeparators.size();

  // That's one of the lines of the log, non including the final carriage return code
  std::string_view line(fileView.substr(startPos, endPos-startPos));
  
  if ((line.find(joinSucceededToken) != line.npos) ||
      (line.find(playerDisconnecctedToken) != line.npos) ||
      (line.find(loginRequestToken) != line.npos) ||
      (line.find(preLoginFailureToken) != line.npos) ||
      (line.find(verifyIdentToken) != line.npos))
  {
    // Player filtering
    m_ServerLogFilePlayersContent.append(line);
    m_ServerLogFilePlayersContent.append("\r\n");

    auto pos = line.find(joinSucceededToken);
    if (pos != line.npos)
    {
      pos += joinSucceededToken.size();
      dw::String playerName = line.substr(pos);
      m_CurrentPlayers.insert(playerName.Strip());
      DW_Print(INFOL_IMPORTANT, e_DedicatedServerLauncher, "%s", line);
    }
    else
    {
      pos = line.find(playerDisconnecctedToken);
      if (pos != line.npos)
      {
        pos += playerDisconnecctedToken.size();
        dw::String playerName = line.substr(pos);
        m_CurrentPlayers.erase(playerName.Strip());
        DW_Print(INFOL_IMPORTANT, e_DedicatedServerLauncher, "%s", line);
      }
    }
  }
  else
  {
    (...)  
    // Chat filtering
    (...)  
    // Mod filtering
    (...)  
    // Log Server Stats
    (...)  
  }
  startPos = fileView.find_first_not_of(lineSeparators, endPos);
}
m_ServerLogFileParsedPosition = lastParsedPosition;

Normally the if/else does not matter because the log is processed line by line.

I tried harcoding something like that:

m_ServerLogFileContent.append("[2022.10.20-08.56.56:349][418]LogNet: Join succeeded: testman#1234\r\n");
m_ServerLogFileContent.append("[2022.10.20-08.56.56:349][418]LogNet: Join succeeded: toolguy#5678\r\n");
m_ServerLogFileContent.append("[2022.10.20-09.39.28:612][590]LogNet: Player disconnected: toolguy#5678\r\n");
m_ServerLogFileContent.append("[2022.10.20-09.39.28:612][590]LogNet: Player disconnected: testman#1234\r\n");

and that was parsed just fine.

Or did I miss something obvious?

:brazil: feedback Conan Exiles servers

:face_with_monocle: Aqui é o ponto que havia dito que está fazendo algo de errado e sim você deve usar as versões atualizadas do DSL para não ter problemas, e com certeza seu problema é que está tentando assoviar e beber água ao mesmo tempo (no Brasil é o mesmo que assoviar e chupar Cana-de-açúcar ao mesmo tempo). Isso que está fazendo já está errado tenho certeza que se usar o DSL de forma correta irá funcionar corretamente nas versões atualizadas ou seja com a versão atual.

:thinking: Pergunta de curiosidade mesmo… Por que diabos está usando tantos DSL ao mesmo tempo se você mesmo diz não ter um computador bom para a função? Está tentando deixar seu servidor igual da g-portal? :sweat_smile:

Hey there. Downloaded DSL 1.7.9
Trying to install server and receiving this.

“Failed running SteamCMD.exe, please check that the executable is present in the 'D:\ConanExilesPVE\DedicatedServerLauncher' folder, or use -basefolder to point to the correct location
ShellExecute returned error code: 42”

I’m clicking ok and then getting

"Failed patching the game server located at ‘D:\ConanExilesPVE\DedicatedServerLauncher\ConanExilesDedicatedServer\ConanSandboxServer.exe’

:brazil: feedback Conan Exiles servers Players Helping Players
Se a função ShellExecuteA no Windows retornar um valor de erro, isso indicará a causa da falha. A função retorna um valor maior que 32 se for bem-sucedida.

Você tem que iniciar e esperar ele abrir porém não deve iniciar antes de abrir as portas de iP do HOST ou da máquina ou computador… Quando ligar “ON” ele abre outra janela que será o servidor do jogo de Conan Exiles e não deverá ser fechada para não desligar “OFF” o servidor

Depois de configurado consegue até mesmo visualizar na steam esses aqui são os que fiz funcionando com DSL

The thing is i’m trying to turn on the server. SteamCMD window shows up for like millisecond and shows off real fast. Few days ago i had everything a-okay with server, turned it on multiple times.

Ports are open, IP is clear and stabilized. I had few servers not too long ago (about 4 months ago)

When i’m opening SteamCMD in the folder which error directing me to - steamCMD opens up normally

Only when i’m using DSL it’s acting strange and allowing me to start server

:brazil: feedback Conan Exiles servers Players Helping Players
Esse da STEAM não funciona e não tem painel DSL e não funciona direito :face_with_monocle: baixe o DSL atualizado para configurar que irá funcionar se for bem configurado :thinking:

That is exactly what i did.
I downloaded DSL from official link. NOT from Steam

1 Like

:brazil: feedback Conan Exiles servers Players Helping Players

Tente abrir o DSL com permissões administrativas poderá resolver algum impedimento que seu PC está tendo para abrir

Tried that too. Didn’t work out. 1.7.5 DSL works normally tho

:brazil: feedback Conan Exiles servers Players Helping Players

Então tente fazer esses passos que no meu país Brasil só funciona se fizer assim porém a configuração pode ajudar você ajustar algo que estiver errado com suas configurações de DSL, porém se não conseguir deve recorrer a @Toolguy para ver a fundo sobre esse problema, mas creio que não seja com o DSL e sim com alguma configuração em sua máquina que está fazendo o HOST

Could that error shows up somehow because i’m from Russia?
All the old launchers except 1.7.9 works absolutely fine. Everything same with my computer as always. I didn’t change, downloaded something else or did anything.
Ports good, IP too. Nothing blocks .exe or launcher itself

:brazil: feedback Conan Exiles servers Players Helping Players

Não creio que tenha qualquer relação com região porém se esse DSL 1.7.9 funciona recomendo que continue nessa versão, eu mesmo citei dentro do link acima uma versão que não funcionava bem para mim, então recomendo que sempre baixe e guarde as versões que funcionam mas o recomendado é usar a versão atual. Se a nova versão não funcionar basta manter a anterior eu mesmo já fiz isso sem problemas

So if 1.7.9 not working for me - i just have to wait till this glitch will be fixed and in the meantime use 1.7.5 launcher, right?

1 Like

:brazil: feedback Conan Exiles servers Players Helping Players

Sim sempre irá funcionar por que o DSL é um painel muito bem ajustado porém ele faz a chamada do CMD de base de servidor steam, então ele irá abrir normalmente se a base steam não tiver alteração por parte da funcom.

There’s no checks of this kind in the codebase.

So 1.7.8 is working fine, but not 1.7.9?

Could you:

  • Start 1.7.8, deploy the server, when the server is running stop it and quit.
  • Start 1.7.9, try to deploy the server, when you get the error, quit.
  • Open the \Logs folder and find the matching DedicatedServerLauncher(1.7.8).2024.10…txt and DedicatedServerLauncher(1.7.9).2024.10…txt log files
  • Send them to me as a private message (or attach here if there’s nothing important) so I can compare the content

So let see what changed between 1.7.8 and 1.7.9


1.7.8:

  • Added +logoff to all the SteamCMD commands to help solve some issues with never ending Steam updates for some players

1.7.9:
Fixed a few issues with the “Run Script” feature:

  • The log now contains a line indicating the script is running (or failing) with the complete path and parameters
  • The blocking dialog is now a Windows notification popup
  • If Discord is configured, you will receive a message there saying that the script could not run

The code handling StreamCMD did not change between the two versions.

The only thing that changed between 1.7.8 and 1.7.9 is the function that runs the backup scripts, but, there may have been some Visual Studio updates, or some dependencies, so hard to say exactly.

:brazil: feedback servers

Fiz uma alteração importante e limpei meu servidor instalando do zero e uma coisa que pode ajudar quem não estiver conseguindo iniciar o DSL é fazer a instalação do directx runtime atualizei a aba da wiki agora informando sobre esse componente importante, sem isso o DSL não irá abrir o servidor CMD :stuck_out_tongue:

Opps - I should have realized the If/else was in a loop testing each line!

Sending DM.

1 Like