Commit 0a1c05e0 authored by Jonathan Michalon's avatar Jonathan Michalon

better handle resources (tiles/tileset) loading and sharing

Now the resources are installed and a lookup is performed to find them from
either (respectively) an environment var, the user's datadir and the
compile-time datadir.
Also the server is responsible for sharing the xml data so that all clients
get the same thing, from him over network.
Also libovcc is responsible for the DTD files (since it's a sort of spec)
while the actual XML data belongs to the server.
parent b20f107f
...@@ -4,6 +4,9 @@ ACLOCAL_AMFLAGS = -I build/m4 -I build ...@@ -4,6 +4,9 @@ ACLOCAL_AMFLAGS = -I build/m4 -I build
pkgconfig_DATA=ovcc.pc pkgconfig_DATA=ovcc.pc
dist_pkgdata_DATA = data/xml/tiles.dtd \
data/xml/tileset.dtd
doc: doc:
$(MAKE) $@ -C docs $(MAKE) $@ -C docs
......
...@@ -11,9 +11,11 @@ AC_CONFIG_HEADERS([config.h]) ...@@ -11,9 +11,11 @@ AC_CONFIG_HEADERS([config.h])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
pkgdatadir="${datadir}/ovcc/"
pkgconfigdir="${libdir}/pkgconfig" pkgconfigdir="${libdir}/pkgconfig"
vapidir="${datadir}/vala/vapi" vapidir="${datadir}/vala/vapi"
ovccincludedir="${includedir}/ovcc" ovccincludedir="${includedir}/ovcc"
AC_SUBST(pkgdatadir)
AC_SUBST(pkgconfigdir) AC_SUBST(pkgconfigdir)
AC_SUBST(vapidir) AC_SUBST(vapidir)
AC_SUBST(ovccincludedir) AC_SUBST(ovccincludedir)
......
<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE tiles SYSTEM "tiles.dtd">
<tiles>
<tile id="t1">
<center>None</center>
<side position="left">
<type>Path</type>
<relations>
<bottom>True</bottom>
</relations>
</side>
<side position="top">
<type>Field</type>
<relations>
<right>True</right>
</relations>
</side>
</tile>
<!-- should be the same as #1 -->
<tile id="t2">
<center>None</center>
<side position="left">
<type>Path</type>
<relations>
<bottom>True</bottom>
</relations>
</side>
<side position="top">
<type>Field</type>
<relations>
<right>True</right>
</relations>
</side>
<side position="right">
<type>Field</type>
<relations>
<top>True</top>
</relations>
</side>
<side position="bottom">
<type>Path</type>
<relations>
<left>True</left>
</relations>
</side>
</tile>
<tile id="t3">
<center>Monastery</center>
<side position="top">
<type>field</type>
<relations>
<right>true</right>
<left>true</left>
</relations>
</side>
<side position="bottom">
<type>path</type>
</side>
</tile>
<tile id="t4">
<side position="top">
<type>City</type>
<relations>
<left>true</left>
<right>true</right>
</relations>
</side>
<side position="bottom">
<type>Field</type>
</side>
</tile>
<tile id="t5">
<side position="right">
<type>City</type>
</side>
<side position="bottom">
<type>Field</type>
</side>
<side position="top">
<type>Field</type>
<relations>
<left>true</left>
</relations>
</side>
</tile>
<tile id="t6">
<side position="left">
<type>City</type>
<relations>
<top>true</top>
<bottom>true</bottom>
<right>true</right>
</relations>
</side>
</tile>
<!--tile id="t3" /-->
</tiles>
<?xml version="1.0" encoding="utf8"?>
<!DOCTYPE tileset SYSTEM "tileset.dtd">
<tileset first="t1">
<name>Example set</name>
<tile id="t1" count="1" />
<tile id="t2" count="1" />
<tile id="t3" count="10" />
<tile id="t4" count="10" />
<tile id="t5" count="10" />
<tile id="t6" count="10" />
</tileset>
...@@ -6,7 +6,8 @@ EXTRA_DIST = hack.h hack.vapi \ ...@@ -6,7 +6,8 @@ EXTRA_DIST = hack.h hack.vapi \
ovcc.h ovcc.vapi ovcc.h ovcc.vapi
libovcc_la_CPPFLAGS = -DG_LOG_DOMAIN=\"LibOVCC\" libovcc_la_CPPFLAGS = -DG_LOG_DOMAIN=\"LibOVCC\"
libovcc_la_CFLAGS = $(LIBOVCC_CFLAGS) libovcc_la_CFLAGS = $(LIBOVCC_CFLAGS) \
-DDATADIR='"$(pkgdatadir)"'
libovcc_la_LIBADD = $(LIBOVCC_LIBS) libovcc_la_LIBADD = $(LIBOVCC_LIBS)
libovcc_la_VALAFLAGS = $(LIBOVCC_VALAFLAGS) --library=ovcc --header=ovcc.h \ libovcc_la_VALAFLAGS = $(LIBOVCC_VALAFLAGS) --library=ovcc --header=ovcc.h \
--vapidir=. --pkg=hack --vapidir=. --pkg=hack
......
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
[CCode (cheader_filename = "hack.h")] [CCode (cheader_filename = "hack.h")]
namespace OVCC namespace OVCC
{ {
[CCode (cprefix = "", lower_case_cprefix = "")]
namespace Config {
public const string DATADIR;
}
[SimpleType] [SimpleType]
[CCode (type_id = "G_TYPE_UINT", [CCode (type_id = "G_TYPE_UINT",
marshaller_type_name = "UINT", marshaller_type_name = "UINT",
......
...@@ -27,8 +27,8 @@ public class OVCC.Network.GamedataMessage : VariantMessage ...@@ -27,8 +27,8 @@ public class OVCC.Network.GamedataMessage : VariantMessage
public uint[] stack_ids = null; public uint[] stack_ids = null;
public string[] player_nicks = null; public string[] player_nicks = null;
public string tiles_filename { get; set; default = null; } public string tiles_data { get; set; default = null; }
public string tileset_filename { get; set; default = null; } public string tileset_data { get; set; default = null; }
public State status { get; set; default = State.OK; } public State status { get; set; default = State.OK; }
...@@ -41,7 +41,7 @@ public class OVCC.Network.GamedataMessage : VariantMessage ...@@ -41,7 +41,7 @@ public class OVCC.Network.GamedataMessage : VariantMessage
public GamedataMessage (uint[] ids, string[] nicks, string tiles, string tileset) public GamedataMessage (uint[] ids, string[] nicks, string tiles, string tileset)
{ {
Object (tiles_filename: tiles, tileset_filename: tileset); Object (tiles_data: tiles, tileset_data: tileset);
stack_ids = ids; stack_ids = ids;
player_nicks = nicks; player_nicks = nicks;
} }
...@@ -50,8 +50,8 @@ public class OVCC.Network.GamedataMessage : VariantMessage ...@@ -50,8 +50,8 @@ public class OVCC.Network.GamedataMessage : VariantMessage
{ {
Variant ids = stack_ids; Variant ids = stack_ids;
Variant nicks = player_nicks; Variant nicks = player_nicks;
return new Variant ("(ivvss)", status, ids, nicks, tiles_filename, tileset_filename); return new Variant ("(ivvss)", status, ids, nicks, tiles_data, tileset_data);
} }
protected override void parse_variant (uint8[] data) protected override void parse_variant (uint8[] data)
{ {
...@@ -64,7 +64,7 @@ public class OVCC.Network.GamedataMessage : VariantMessage ...@@ -64,7 +64,7 @@ public class OVCC.Network.GamedataMessage : VariantMessage
status = s; status = s;
stack_ids = (uint[]) ids; stack_ids = (uint[]) ids;
player_nicks = (string[]) nicks; player_nicks = (string[]) nicks;
tiles_filename = t; tiles_data = t;
tileset_filename = ts; tileset_data = ts;
} }
} }
...@@ -148,7 +148,7 @@ namespace OVCC.Network ...@@ -148,7 +148,7 @@ namespace OVCC.Network
throws Error throws Error
{ {
var len = stream.read_uint16 (cancel); var len = stream.read_uint16 (cancel);
if (len < 1 || len > 0x400 /* arbitrary limit not to fulfill memory */) { if (len < 1 || len > 0x4000 /* arbitrary limit not to fulfill memory */) {
throw new MessageError.MALFORMED_MESSAGE ("Invalid message length %u", throw new MessageError.MALFORMED_MESSAGE ("Invalid message length %u",
len); len);
} }
......
...@@ -259,21 +259,16 @@ namespace OVCC ...@@ -259,21 +259,16 @@ namespace OVCC
return true; return true;
} }
public bool load (TilesDef tiles, public bool load_from_string (TilesDef tiles,
File file) string data)
throws Error throws Error
{ {
uint8[] data; File dtd = Utils.lookup_resource_path("tileset.dtd");
var doc = Xml.Parser.read_memory (data, data.length, dtd.get_path(), null,
var uri = file.get_uri ();
debug ("Loading tileset file %s...", uri);
file.load_contents (null, out data, null);
var doc = Xml.Parser.read_memory ((string)data, data.length, uri, null,
Xml.ParserOption.DTDVALID | Xml.ParserOption.DTDVALID |
Xml.ParserOption.PEDANTIC); Xml.ParserOption.PEDANTIC);
if (doc == null) { if (doc == null) {
throw new MarkupError.INVALID_CONTENT ("""File "%s" is not valid XML""", throw new MarkupError.INVALID_CONTENT ("""Data is not valid XML""");
uri);
} else { } else {
if (! this.xml_read_tileset_doc (doc->get_root_element (), tiles)) { if (! this.xml_read_tileset_doc (doc->get_root_element (), tiles)) {
warning ("Document loading failed"); warning ("Document loading failed");
...@@ -282,5 +277,17 @@ namespace OVCC ...@@ -282,5 +277,17 @@ namespace OVCC
return true; return true;
} }
public bool load (TilesDef tiles,
string filename)
throws Error
{
uint8[] data;
File file = Utils.lookup_resource_path(filename);
debug ("""Loading tileset definitions from "%s"...""", file.get_path());
file.load_contents (null, out data, null);
return load_from_string (tiles, (string)data);
}
} }
} }
...@@ -235,20 +235,15 @@ namespace OVCC ...@@ -235,20 +235,15 @@ namespace OVCC
return true; return true;
} }
public bool load (File file) public bool load_from_string (string data)
throws Error throws Error
{ {
uint8[] data; File dtd = Utils.lookup_resource_path("tiles.dtd");
var doc = Xml.Parser.read_memory (data, data.length, dtd.get_path(), null,
var uri = file.get_uri ();
debug ("""Loading tiles definitions from "%s"...""", uri);
file.load_contents (null, out data, null);
var doc = Xml.Parser.read_memory ((string)data, data.length, uri, null,
Xml.ParserOption.DTDVALID | Xml.ParserOption.DTDVALID |
Xml.ParserOption.PEDANTIC); Xml.ParserOption.PEDANTIC);
if (doc == null) { if (doc == null) {
throw new MarkupError.INVALID_CONTENT ("""File "%s" is not valid XML""", throw new MarkupError.INVALID_CONTENT ("""Data is not valid XML""");
uri);
} else { } else {
if (! this.xml_read_doc (doc->get_root_element ())) { if (! this.xml_read_doc (doc->get_root_element ())) {
warning ("Document loading failed"); warning ("Document loading failed");
...@@ -257,6 +252,17 @@ namespace OVCC ...@@ -257,6 +252,17 @@ namespace OVCC
return true; return true;
} }
public bool load (string filename)
throws Error
{
uint8[] data;
File file = Utils.lookup_resource_path(filename);
debug ("""Loading tiles definitions from "%s"...""", file.get_path());
file.load_contents (null, out data, null);
return load_from_string ((string)data);
}
} }
} }
...@@ -93,4 +93,31 @@ namespace OVCC.Utils ...@@ -93,4 +93,31 @@ namespace OVCC.Utils
return (uint)val; return (uint)val;
} }
/**
* Lookup a filename in common and custom resource prefixes
*
* @param str The filename to look for
* @return a File object
*/
public File lookup_resource_path (string str)
throws FileError
{
List<string> list = new List<string> ();
list.prepend(Config.DATADIR);
list.prepend(Path.build_filename(Environment.get_user_data_dir(), "ovcc"));
list.prepend(Environment.get_variable ("OVCC_DATADIR"));
foreach (var d in list)
{
if (d != null)
{
debug ("Looking up for %s in %s", str, d);
File file = File.new_for_path(Path.build_filename(d, str));
if (file.query_exists())
return file;
}
}
throw new FileError.NOENT ("Resource not found in common and custom paths");
}
} }
...@@ -484,16 +484,16 @@ namespace OVCCClient ...@@ -484,16 +484,16 @@ namespace OVCCClient
/* create our game */ /* create our game */
try { try {
tiles.load (File.new_for_path (data.tiles_filename)); tiles.load_from_string (data.tiles_data);
} catch (Error e1) { } catch (Error e1) {
throw new ServerError.DATA_UNREACHABLE ("Failed to load tiles data from \"%s\": %s", throw new ServerError.DATA_UNREACHABLE ("Failed to load tiles data from server: %s",
data.tiles_filename, e1.message); e1.message);
} }
try { try {
tileset.load (tiles, File.new_for_path (data.tileset_filename)); tileset.load_from_string (tiles, data.tileset_data);
} catch (Error e2) { } catch (Error e2) {
throw new ServerError.DATA_UNREACHABLE ("Failed to load tileset data from \"%s\": %s", throw new ServerError.DATA_UNREACHABLE ("Failed to load tileset data from server: %s",
data.tileset_filename, e2.message); e2.message);
} }
var stack = new Stack.from_tile_ids (data.stack_ids, tiles); var stack = new Stack.from_tile_ids (data.stack_ids, tiles);
......
SUBDIRS = src SUBDIRS = src
dist_pkgdata_DATA = data/xml/tiles.xml \
data/xml/tileset.xml
ACLOCAL_AMFLAGS = -I build/m4 ACLOCAL_AMFLAGS = -I build/m4
...@@ -11,6 +11,9 @@ AC_CONFIG_HEADERS([config.h]) ...@@ -11,6 +11,9 @@ AC_CONFIG_HEADERS([config.h])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
pkgdatadir="${datadir}/ovcc/"
AC_SUBST(pkgdatadir)
# Checks for programs. # Checks for programs.
AC_PROG_CC AC_PROG_CC
AM_PROG_VALAC([0.11.6]) AM_PROG_VALAC([0.11.6])
......
...@@ -32,6 +32,8 @@ public class Server: ThreadedSocketService ...@@ -32,6 +32,8 @@ public class Server: ThreadedSocketService
private List<Game> tables = null; private List<Game> tables = null;
private TilesDef tiles = new TilesDef (); private TilesDef tiles = new TilesDef ();
private TileSet tileset = new TileSet (); private TileSet tileset = new TileSet ();
private string tiles_data;
private string tileset_data;
public string name { get; construct; } public string name { get; construct; }
public uint port { get; construct; default = 0xdead; } public uint port { get; construct; default = 0xdead; }
...@@ -148,7 +150,7 @@ public class Server: ThreadedSocketService ...@@ -148,7 +150,7 @@ public class Server: ThreadedSocketService
debug ("Sending game data"); debug ("Sending game data");
var data = new GamedataMessage (client.game.get_stack_ids(), var data = new GamedataMessage (client.game.get_stack_ids(),
client.game.get_player_nicks(), client.game.get_player_nicks(),
TILES_FILE, TILESET_FILE); tiles_data, tileset_data);
client.send (data); client.send (data);
return true; return true;
} }
...@@ -241,10 +243,21 @@ public class Server: ThreadedSocketService ...@@ -241,10 +243,21 @@ public class Server: ThreadedSocketService
uint16 port = 0xdead) uint16 port = 0xdead)
throws Error throws Error
{ {
uint8[] data1;
uint8[] data2;
Object (name: name, port: port); Object (name: name, port: port);
tiles.load (File.new_for_path (TILES_FILE)); File tiles_file = Utils.lookup_resource_path(TILES_FILE);
tileset.load (tiles, File.new_for_path (TILESET_FILE)); File tileset_file = Utils.lookup_resource_path(TILESET_FILE);
tiles_file.load_contents(null, out data1, null);
tileset_file.load_contents(null, out data2, null);
tiles_data = (string) data1;
tileset_data = (string) data2;
tiles.load_from_string (tiles_data);
tileset.load_from_string (tiles, tileset_data);
} }
/* a bit ugly since a direct call to parent method will not trigger the /* a bit ugly since a direct call to parent method will not trigger the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment