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
pkgconfig_DATA=ovcc.pc
dist_pkgdata_DATA = data/xml/tiles.dtd \
data/xml/tileset.dtd
doc:
$(MAKE) $@ -C docs
......
......@@ -11,9 +11,11 @@ AC_CONFIG_HEADERS([config.h])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
pkgdatadir="${datadir}/ovcc/"
pkgconfigdir="${libdir}/pkgconfig"
vapidir="${datadir}/vala/vapi"
ovccincludedir="${includedir}/ovcc"
AC_SUBST(pkgdatadir)
AC_SUBST(pkgconfigdir)
AC_SUBST(vapidir)
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 \
ovcc.h ovcc.vapi
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_VALAFLAGS = $(LIBOVCC_VALAFLAGS) --library=ovcc --header=ovcc.h \
--vapidir=. --pkg=hack
......
......@@ -21,6 +21,11 @@
[CCode (cheader_filename = "hack.h")]
namespace OVCC
{
[CCode (cprefix = "", lower_case_cprefix = "")]
namespace Config {
public const string DATADIR;
}
[SimpleType]
[CCode (type_id = "G_TYPE_UINT",
marshaller_type_name = "UINT",
......
......@@ -27,8 +27,8 @@ public class OVCC.Network.GamedataMessage : VariantMessage
public uint[] stack_ids = null;
public string[] player_nicks = null;
public string tiles_filename { get; set; default = null; }
public string tileset_filename { get; set; default = null; }
public string tiles_data { get; set; default = null; }
public string tileset_data { get; set; default = null; }
public State status { get; set; default = State.OK; }
......@@ -41,7 +41,7 @@ public class OVCC.Network.GamedataMessage : VariantMessage
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;
player_nicks = nicks;
}
......@@ -50,8 +50,8 @@ public class OVCC.Network.GamedataMessage : VariantMessage
{
Variant ids = stack_ids;
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)
{
......@@ -64,7 +64,7 @@ public class OVCC.Network.GamedataMessage : VariantMessage
status = s;
stack_ids = (uint[]) ids;
player_nicks = (string[]) nicks;
tiles_filename = t;
tileset_filename = ts;
tiles_data = t;
tileset_data = ts;
}
}
......@@ -148,7 +148,7 @@ namespace OVCC.Network
throws Error
{
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",
len);
}
......
......@@ -259,21 +259,16 @@ namespace OVCC
return true;
}
public bool load (TilesDef tiles,
File file)
public bool load_from_string (TilesDef tiles,
string data)
throws Error
{
uint8[] data;
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,
File dtd = Utils.lookup_resource_path("tileset.dtd");
var doc = Xml.Parser.read_memory (data, data.length, dtd.get_path(), null,
Xml.ParserOption.DTDVALID |
Xml.ParserOption.PEDANTIC);
if (doc == null) {
throw new MarkupError.INVALID_CONTENT ("""File "%s" is not valid XML""",
uri);
throw new MarkupError.INVALID_CONTENT ("""Data is not valid XML""");
} else {
if (! this.xml_read_tileset_doc (doc->get_root_element (), tiles)) {
warning ("Document loading failed");
......@@ -282,5 +277,17 @@ namespace OVCC
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
return true;
}
public bool load (File file)
public bool load_from_string (string data)
throws Error
{
uint8[] data;
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,
File dtd = Utils.lookup_resource_path("tiles.dtd");
var doc = Xml.Parser.read_memory (data, data.length, dtd.get_path(), null,
Xml.ParserOption.DTDVALID |
Xml.ParserOption.PEDANTIC);
if (doc == null) {
throw new MarkupError.INVALID_CONTENT ("""File "%s" is not valid XML""",
uri);
throw new MarkupError.INVALID_CONTENT ("""Data is not valid XML""");
} else {
if (! this.xml_read_doc (doc->get_root_element ())) {
warning ("Document loading failed");
......@@ -257,6 +252,17 @@ namespace OVCC
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
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
/* create our game */
try {
tiles.load (File.new_for_path (data.tiles_filename));
tiles.load_from_string (data.tiles_data);
} catch (Error e1) {
throw new ServerError.DATA_UNREACHABLE ("Failed to load tiles data from \"%s\": %s",
data.tiles_filename, e1.message);
throw new ServerError.DATA_UNREACHABLE ("Failed to load tiles data from server: %s",
e1.message);
}
try {
tileset.load (tiles, File.new_for_path (data.tileset_filename));
tileset.load_from_string (tiles, data.tileset_data);
} catch (Error e2) {
throw new ServerError.DATA_UNREACHABLE ("Failed to load tileset data from \"%s\": %s",
data.tileset_filename, e2.message);
throw new ServerError.DATA_UNREACHABLE ("Failed to load tileset data from server: %s",
e2.message);
}
var stack = new Stack.from_tile_ids (data.stack_ids, tiles);
......
SUBDIRS = src
dist_pkgdata_DATA = data/xml/tiles.xml \
data/xml/tileset.xml
ACLOCAL_AMFLAGS = -I build/m4
......@@ -11,6 +11,9 @@ AC_CONFIG_HEADERS([config.h])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
pkgdatadir="${datadir}/ovcc/"
AC_SUBST(pkgdatadir)
# Checks for programs.
AC_PROG_CC
AM_PROG_VALAC([0.11.6])
......
......@@ -32,6 +32,8 @@ public class Server: ThreadedSocketService
private List<Game> tables = null;
private TilesDef tiles = new TilesDef ();
private TileSet tileset = new TileSet ();
private string tiles_data;
private string tileset_data;
public string name { get; construct; }
public uint port { get; construct; default = 0xdead; }
......@@ -148,7 +150,7 @@ public class Server: ThreadedSocketService
debug ("Sending game data");
var data = new GamedataMessage (client.game.get_stack_ids(),
client.game.get_player_nicks(),
TILES_FILE, TILESET_FILE);
tiles_data, tileset_data);
client.send (data);
return true;
}
......@@ -241,10 +243,21 @@ public class Server: ThreadedSocketService
uint16 port = 0xdead)
throws Error
{
uint8[] data1;
uint8[] data2;
Object (name: name, port: port);
tiles.load (File.new_for_path (TILES_FILE));
tileset.load (tiles, File.new_for_path (TILESET_FILE));
File tiles_file = Utils.lookup_resource_path(TILES_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
......
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