Skip to content

Commit

Permalink
Testing dxml, too restrictive for now
Browse files Browse the repository at this point in the history
  • Loading branch information
torhus committed Dec 18, 2022
1 parent 3d76968 commit 766851b
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 154 deletions.
1 change: 1 addition & 0 deletions dub.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ copyright "Copyright © 2022, torhu"
#dependency "dwt" version="~>1.0.5"
dependency "dwt" repository="git+https://github.com/torhus/dwt.git" \
version="19f219e"
dependency "dxml" version="~>0.4.3"
dependency "fswatch" version="~>0.6.0"
dependency "undead" version="~>1.1.7"
targetName "MonsterBrowser"
Expand Down
292 changes: 139 additions & 153 deletions src/masterlist.d
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import std.file;
import std.path;
import std.stdio;
import std.uni;

import dxml.parser;
import dxml.util;
import tango.text.xml.DocEntity;
import tango.text.xml.SaxParser;

Expand Down Expand Up @@ -215,19 +218,13 @@ final class MasterList

char[] content = cast(char[])read(dataDir ~ fileName_);
GC.setAttr(content.ptr, GC.BlkAttr.NO_SCAN);
auto parser = new SaxParser!(char);
auto handler = new MySaxHandler!(char)(defaultProtocolVersion);

parser.setSaxHandler(handler);
parser.setContent(content);
parser.parse;

log("Loaded %s servers in %s seconds.", handler.servers.length,
timer.seconds);

synchronized (this) {
servers_ = handler.servers;
downCount_ = handler.downCount;
servers_.clear;
downCount_ = 0;
parse(content, defaultProtocolVersion);
log("Loaded %s servers in %s seconds.", servers_.length,
timer.seconds);
}

return true;
Expand Down Expand Up @@ -266,6 +263,137 @@ final class MasterList
return true;
}

private void parse(in char[] content, string defaultProtocolVersion)
{
ServerData sd;

void addCvar(R)(R attributes) if (isAttrRange!R)
{
string[] cvar = new string[2];

foreach (attr; attributes) {
if (attr.name == "key")
cvar[0] = decodeXML(attr.value);
else if (attr.name == "value")
cvar[1] = decodeXML(attr.value);
}

sd.cvars ~= cvar;
}

void addPlayer(R)(R attributes) if (isAttrRange!R)
{
string[] player = new string[PlayerColumn.max + 1];

foreach (attr; attributes) {
if (attr.name == "name")
player[PlayerColumn.RAWNAME] = decodeXML(attr.value);
else if (attr.name == "score")
player[PlayerColumn.SCORE] = decodeXML(attr.value);
else if (attr.name == "ping")
player[PlayerColumn.PING] = decodeXML(attr.value);
}

sd.players ~= player;
}

void startServer(R)(R attributes) if (isAttrRange!R)
{
sd.server.length = ServerColumn.max + 1;

foreach (attr; attributes) {
switch (attr.name) {
case "name":
sd.rawName = decodeXML(attr.value);
sd.server[ServerColumn.NAME] =
(stripColorCodes(decodeXML(attr.value)));
break;
case "country_code":
sd.server[ServerColumn.COUNTRY] = decodeXML(attr.value);
break;
case "address":
sd.server[ServerColumn.ADDRESS] = decodeXML(attr.value);
break;
case "protocol_version":
sd.protocolVersion = decodeXML(attr.value);
break;
case "ping":
sd.server[ServerColumn.PING] = decodeXML(attr.value);
break;
case "player_count":
sd.server[ServerColumn.PLAYERS] = decodeXML(attr.value);
break;
case "map":
sd.server[ServerColumn.MAP] = decodeXML(attr.value);
break;
case "persistent":
sd.persistent = attr.value == "true";
break;
case "fail_count":
sd.failCount = toIntOrDefault(attr.value);
break;
default:
break;
}
}
// Make sure there's a protocol version. This makes it less likely the
// server is being 'forgotten' and never queried or deleted.
// It also takes care of upgrading from the old XML files, where there
// were no protocol_version attribute.
if (sd.protocolVersion.length == 0)
sd.protocolVersion = defaultProtocolVersion;
}

void endServer()
{
auto cvars = sd.cvars;
sortStringArray(cvars);

if (auto cvar = cvars.getCvar("g_gametype")) {
sd.server[ServerColumn.GAMETYPE_NUM] = cvar[1];
sd.numericGameType = toIntOrDefault(cvar[1], -1);
}
if (auto cvar = cvars.getCvar("g_needpass")) {
string s = cvar[1] == "0" ? PASSWORD_NO : PASSWORD_YES;
sd.server[ServerColumn.PASSWORDED] = s;
}
if (auto cvar = cvars.getCvar("game")) {
sd.server[ServerColumn.CVAR_GAME] = cvar[1];
}
if (auto cvar = cvars.getCvar("gamename")) {
sd.server[ServerColumn.CVAR_GAMENAME] = cvar[1];
}

if (!hasReplied(&sd))
downCount_++;

servers_[sd.server[ServerColumn.ADDRESS]] = sd;
sd = ServerData.init;
}

enum config = makeConfig(SplitEmpty.yes, SkipComments.yes);
foreach(entity; parseXML!config(content)) {
if (entity.type == EntityType.elementStart) {
switch (entity.name) {
case "cvar":
addCvar(entity.attributes);
break;
case "player":
addPlayer(entity.attributes);
break;
case "server":
startServer(entity.attributes);
break;
default:
break;
}
}
else if (entity.type == EntityType.elementEnd &&
entity.name == "server") {
endServer();
}
}
}

/*invariant()
{
Expand Down Expand Up @@ -378,145 +506,3 @@ private final class XmlDumper
File output_;
}
}


private final class MySaxHandler(Ch=char) : SaxHandler!(Ch)
{
ServerData[string] servers;
ServerData sd;
size_t downCount = 0;

private string defaultProtocolVersion_;


this(string defaultProtocolVersion)
{
defaultProtocolVersion_ = defaultProtocolVersion;
}

override void startElement(const(Ch)[] uri, const(Ch)[] localName,
const(Ch)[] qName, Attribute!(Ch)[] attributes)
{
if (localName == "cvar")
addCvar(attributes);
else if (localName == "player")
addPlayer(attributes);
else if (localName == "server")
startServer(attributes);
}


override void endElement(const(Ch)[] uri, const(Ch)[] localName,
const(Ch)[] qName)
{
if (localName == "server") {
auto cvars = sd.cvars;

sortStringArray(cvars);

if (auto cvar = cvars.getCvar("g_gametype")) {
sd.server[ServerColumn.GAMETYPE_NUM] = cvar[1];
sd.numericGameType = toIntOrDefault(cvar[1], -1);
}
if (auto cvar = cvars.getCvar("g_needpass")) {
string s = cvar[1] == "0" ? PASSWORD_NO : PASSWORD_YES;
sd.server[ServerColumn.PASSWORDED] = s;
}
if (auto cvar = cvars.getCvar("game")) {
sd.server[ServerColumn.CVAR_GAME] = cvar[1];
}
if (auto cvar = cvars.getCvar("gamename")) {
sd.server[ServerColumn.CVAR_GAMENAME] = cvar[1];
}

if (!hasReplied(&sd))
downCount++;

servers[sd.server[ServerColumn.ADDRESS]] = sd;
sd = ServerData.init;
}
}


// Allocate a new server and add server attributes.
private void startServer(Attribute!(Ch)[] attributes)
{
sd.server.length = ServerColumn.max + 1;

foreach (ref attr; attributes) {
if (attr.localName == "name") {
sd.rawName = fromEntityCopy(attr.value);
sd.server[ServerColumn.NAME] =
cast(string)fromEntity(stripColorCodes(attr.value));
}
else if (attr.localName == "country_code")
sd.server[ServerColumn.COUNTRY] = fromEntityCopy(attr.value);
else if (attr.localName == "address")
sd.server[ServerColumn.ADDRESS] = fromEntityCopy(attr.value);
else if (attr.localName == "protocol_version")
sd.protocolVersion = fromEntityCopy(attr.value);
else if (attr.localName == "ping")
sd.server[ServerColumn.PING] = fromEntityCopy(attr.value);
else if (attr.localName == "player_count")
sd.server[ServerColumn.PLAYERS] = fromEntityCopy(attr.value);
else if (attr.localName == "map")
sd.server[ServerColumn.MAP] = fromEntityCopy(attr.value);
else if (attr.localName == "persistent")
sd.persistent = attr.value == "true";
else if (attr.localName == "fail_count")
sd.failCount = toIntOrDefault(attr.value);
}

// Make sure there's a protocol version. This makes it less likely the
// server is being 'forgotten' and never queried or deleted.
// It also takes care of upgrading from the old XML files, where there
// were no protocol_version attribute.
if (sd.protocolVersion.length == 0)
sd.protocolVersion = defaultProtocolVersion_;
}


// Add a cvar.
private void addCvar(Attribute!(Ch)[] attributes)
{
string[] cvar = new string[2];

foreach (ref attr; attributes) {
if (attr.localName == "key")
cvar[0] = fromEntityCopy(attr.value);
else if (attr.localName == "value")
cvar[1] = fromEntityCopy(attr.value);
}

sd.cvars ~= cvar;
}


// Add a player.
private void addPlayer(Attribute!(Ch)[] attributes)
{
string[] player = new string[PlayerColumn.max + 1];

foreach (ref attr; attributes) {
if (attr.localName == "name")
player[PlayerColumn.RAWNAME] = fromEntityCopy(attr.value);
else if (attr.localName == "score")
player[PlayerColumn.SCORE] = fromEntityCopy(attr.value);
else if (attr.localName == "ping")
player[PlayerColumn.PING] = fromEntityCopy(attr.value);
}

sd.players ~= player;
}

// Convert XML entities to characters, unconditionally copying the source.
string fromEntityCopy(in char[] s)
{
const(char)[] r = fromEntity(s);
if (r.ptr != s.ptr)
return cast(string)r;
else
return s.idup;
}

}
5 changes: 4 additions & 1 deletion src/serveractions.d
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import std.conv;
import std.file;
import std.stdio;
import std.string;

import dxml.parser : XMLParsingException;
import tango.text.xml.PullParser : XmlException;

import java.lang.Runnable;
Expand Down Expand Up @@ -118,8 +120,9 @@ void switchToGame(string name, bool configChanged=false)
catch (FileException e) {
error("There was an error reading " ~ master.fileName);
}
catch (XmlException e) {
catch (XMLParsingException e) {
error("Syntax error in " ~ master.fileName);
logx(__FILE__, __LINE__, e);
}

}
Expand Down

0 comments on commit 766851b

Please sign in to comment.