diff options
author | Nathan Letwory <nathan@letworyinteractive.com> | 2006-10-12 15:53:50 +0400 |
---|---|---|
committer | Nathan Letwory <nathan@letworyinteractive.com> | 2006-10-12 15:53:50 +0400 |
commit | b2a8417fce8d231188ee75062804be4d471c57be (patch) | |
tree | 86d163d10a0f486fc203e0c95619c6f2b06aa6b1 /extern | |
parent | 86e192ea40ececcade256e528c557d9018751cb8 (diff) |
Add Verse master-server functionality
* added two files from verse-master
* server list is available in outliner (new mode "Verse Servers")
* verse sessions are now also in new mode "Verse Sessions" in outliner
* fixed drawing of verse sessions and their nodes
* in user preferences System & OpenGL master-server ip setting (default master.uni-verse.org)
* in File>Verse entry "Get Servers" to get server list or
* RMB on "Available Verse Servers" in outliner to "Refresh" server list
Enjoy :)
Diffstat (limited to 'extern')
-rw-r--r-- | extern/verse/dist/Makefile | 3 | ||||
-rw-r--r-- | extern/verse/dist/SConstruct | 6 | ||||
-rw-r--r-- | extern/verse/dist/verse_ms.c | 286 | ||||
-rw-r--r-- | extern/verse/dist/verse_ms.h | 72 | ||||
-rw-r--r-- | extern/verse/make/msvc_7_0/libverse.vcproj | 6 |
5 files changed, 370 insertions, 3 deletions
diff --git a/extern/verse/dist/Makefile b/extern/verse/dist/Makefile index 34cc10603b4..166fa1fd69c 100644 --- a/extern/verse/dist/Makefile +++ b/extern/verse/dist/Makefile @@ -30,7 +30,8 @@ LIBVERSE_SRC = $(PROT_OUT) v_bignum.c v_cmd_buf.c v_connect.c \ v_connection.c v_connection.h v_encryption.c \ v_func_storage.c v_internal_verse.h v_man_pack_node.c \ v_network.c v_network.h v_network_in_que.c v_network_out_que.c \ - v_pack.c v_pack.h v_pack_method.c v_prime.c v_randgen.c v_util.c + v_pack.c v_pack.h v_pack_method.c v_prime.c v_randgen.c v_util.c \ + verse_ms.c LIBVERSE_OBJ = $(patsubst %h,, $(LIBVERSE_SRC:%.c=%.o)) diff --git a/extern/verse/dist/SConstruct b/extern/verse/dist/SConstruct index d54f299c3f6..b0b9ed3ef2f 100644 --- a/extern/verse/dist/SConstruct +++ b/extern/verse/dist/SConstruct @@ -110,7 +110,8 @@ lib_source_files = (['v_cmd_buf.c', 'v_prime.c', 'v_randgen.c', 'v_util.c', - 'v_bignum.c' + 'v_bignum.c', + 'verse_ms.c' ]) lib_source_files.extend(cmd_gen_deps) @@ -135,10 +136,11 @@ verselib_env.Append(CPPDEFINES = defines) verseserver_env = verse_env.Copy() verseserver_env.Append(CPPDEFINES = defines) verseserver_env.Append (LIBPATH = ['.']) +verseserver_env.Append (LIBS= ['verse']) verseserver_env.Append (LIBS= platform_libs) verselib_env.BlenderLib(libname='verse', sources=lib_source_files, includes=["."], defines = defines, libtype=['core', 'intern'], priority = [5, 5]) -verseserver_env.BlenderProg(builddir="#"+root_build_dir+os.sep, progname='verse', sources=server_source_files, libs=['verse'], +verseserver_env.BlenderProg(builddir="#"+root_build_dir+os.sep, progname='verse', sources=server_source_files, libs=[], libpath='#'+env['BF_BUILDDIR']+'/lib') diff --git a/extern/verse/dist/verse_ms.c b/extern/verse/dist/verse_ms.c new file mode 100644 index 00000000000..84f3fdb837b --- /dev/null +++ b/extern/verse/dist/verse_ms.c @@ -0,0 +1,286 @@ +/* + * A helper library to send and parse master server pings. See the relevant + * header for details. + * + * This code was written in 2006 by Emil Brink. It is released as public domain. +*/ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "verse.h" +#include "verse_ms.h" + +/* Build and send a MS:GET packet. */ +void verse_ms_get_send(const char *address, int fields, const char *tags) +{ + char req[128]; + + strcpy(req, "MS:GET IP="); + if(fields & VERSE_MS_FIELD_DESCRIPTION) + strcat(req, "DE"); + if(tags != NULL) + { + strcat(req, " TA="); + strcat(req, tags); + } + verse_send_ping(address, req); +} + +/* Skip assign, i.e. "NAME=" string, at <msg>. Stores name into <put>, and then updates + * it. Returns NULL on parse error, in which case the <put> pointer is not advanced. +*/ +static const char * skip_assign(char **put, const char *msg) +{ + if(isalpha(*msg)) + { + char *p = put != NULL ? *put : NULL; + + if(p != NULL) + *p++ = *msg; + msg++; + while(*msg && (isalnum(*msg) || *msg == '_')) + { + if(p != NULL) + *p++ = *msg; + msg++; + } + if(*msg == '=') + { + if(p != NULL) + *p++ = '\0'; + if(put != NULL) + *put = p; + return msg + 1; + } + } + return NULL; +} + +/** Skip value at <msg>, optionally storing de-quoted version through <put>, + * which is advanced. Returns NULL on parse error, without updating <put>. +*/ +static const char * skip_value(char **put, const char *msg) +{ + char *p = (put != NULL) ? *put : NULL; + + if(*msg == '"') + { + msg++; + while(*msg != '\0' && *msg != '"') + { + if(*msg == '\\') + { + if(msg[1] != '\0') + msg++; + else + return NULL; + } + if(p != NULL) + *p++ = *msg; + msg++; + } + if(*msg == '"') + { + if(p != NULL) + *p++ = '\0'; + if(put != NULL) + *put = p; + msg++; + if(*msg == '\0' || isspace(*msg)) + return msg; + } + return NULL; + } + while(*msg && !isspace(*msg)) + { + if(*msg == '"') + return NULL; + if(p != NULL) + *p++ = *msg; + msg++; + } + if(p != NULL) + *p++ = '\0'; + if(put != NULL) + *put = p; + return msg; +} + +static const char * put_field(VMSField *field, char **put, const char *src) +{ + const char *ptr; + char *base = *put; + + if((ptr = skip_assign(put, src)) != NULL && ptr - src > 1) + { + field->name = base; + src = ptr; + base = *put; + if((ptr = skip_value(put, src)) != NULL) + { + field->value = base; + return ptr; + } + } + return NULL; +} + +static int cmp_fields(const void *a, const void *b) +{ + return strcmp(((const VMSField *) a)->name, ((const VMSField *) b)->name); +} + +VMSServer ** verse_ms_list_parse(const char *msg) +{ + const char *word[384]; /* Takes quite a lot of stack space. */ + const char *ptr; + char *put; + size_t num_word = 0, i, j, num_ip = 0, num_field, space = 0; + VMSServer **desc, *next; + VMSField *field; + + if(strncmp(msg, "MS:LIST", 7) == 0) + msg += 7; + if(*msg != ' ') + return NULL; + + /* Step one: split the string into words, at whitespace. Split is aware + * of quoting rules for value assignment, this is crucial. This split is + * non-invasive, meaning each "word" will be a suffix. + */ + while(*msg) + { + while(isspace(*msg)) + msg++; + ptr = skip_assign(NULL, msg); + if(ptr != NULL) + { + space += ptr - msg; + word[num_word++] = msg; + msg = ptr; + ptr = skip_value(NULL, msg); + if(ptr == NULL) + { + fprintf(stderr, "Parse error\n"); + return NULL; + } + space += ptr - msg + 1; + msg = ptr; + } + else if(*msg != '\0') + { + fprintf(stderr, "Parse error\n"); + return NULL; + } + } + /* Now, count how many words begin with "IP=". */ + for(i = 0; i < num_word; i++) + { + if(strncmp(word[i], "IP=", 3) == 0) + num_ip++; + } +/* printf("found %u IPs, %u bytes\n", num_ip, space); + printf("%u IP and %u words -> %u fields total\n", num_ip, num_word, num_word - num_ip); +*/ num_field = num_word - num_ip; + /* Allocate the descriptions. */ +/* printf("allocating %u bytes\n", (num_ip + 1) * (sizeof *desc) + num_ip * sizeof **desc + num_field * sizeof (VMSField) + space); + printf(" %u for pointers, %u for structs, %u for fields, %u string\n", + (num_ip + 1) * (sizeof *desc), num_ip * sizeof **desc, num_field * sizeof (VMSField), space); +*/ desc = malloc((num_ip + 1) * (sizeof *desc) + num_ip * sizeof **desc + num_field * sizeof (VMSField) + space); + next = (VMSServer *) (desc + (num_ip + 1)); +/* printf("desc store at %u\n", (char *) next - (char *) desc);*/ + field = (VMSField *) (next + num_ip); +/* printf("field store at %u\n", (char *) field - (char *) desc);*/ + put = (char *) (field + num_field); +/* printf("string store at %u\n", put - (char *) desc);*/ + for(i = j = 0; i < num_word;) + { + if(strncmp(word[i], "IP=", 3) == 0) + { + desc[j] = next; + next->ip = put; + ptr = skip_value(&put, word[i] + 3); + next->num_fields = 0; + next->field = field; + for(i++; i < num_word && strncmp(word[i], "IP=", 3) != 0; i++, next->num_fields++, field++) + put_field(&next->field[next->num_fields], &put, word[i]); + if(next->num_fields > 0) /* Sort the fields, for binary search later. */ + qsort(next->field, next->num_fields, sizeof *next->field, cmp_fields); + j++; + next++; + } + else + i++; + } + desc[j] = NULL; + return desc; +} + +/* A binary search, exploiting that the fields are sorted. */ +static const VMSField * field_find(const VMSServer *ms, const char *name) +{ + int lo, hi, mid, rel; + + if(ms == NULL || name == NULL) + return NULL; + lo = 0; + hi = ms->num_fields; + while(lo <= hi) + { + mid = (lo + hi) / 2; + rel = strcmp(name, ms->field[mid].name); + if(rel == 0) + return &ms->field[mid]; + if(rel < 0) + hi = mid - 1; + else + lo = mid + 1; + } + return NULL; +} + +int verse_ms_field_exists(const VMSServer *ms, const char *name) +{ + if(ms == NULL || name == NULL) + return 0; + return field_find(ms, name) != NULL; +} + +const char * verse_ms_field_value(const VMSServer *ms, const char *name) +{ + const VMSField *f; + + if((f = field_find(ms, name)) != NULL) + return f->value; + return NULL; +} + +#if defined VERSE_MS_STANDALONE + +int main(void) +{ + VMSServer **servers = verse_ms_list_parse("MS:LIST IP=127.0.0.1:4951 DE=\"A test server, mainly for Eskil\" COOL=yes BACKUP=daily LANG=sv_SE " + "IP=130.237.221.74 DE=\"Test server on a puny laptop\" COOL=yes DORKY=no OPEN=absolutely " + "IP=127.0.0.1:5151 DE=\"This is a back slash: '\\\\', cool huh?\" " + "IP=127.0.0.1:6676 DE=\"a quote looks like this: \\\"\" IP=127.0.0.1:1122 "); + + if(servers != NULL) + { + int i, j; + + printf("Server info:\n"); + for(i = 0; servers[i] != NULL; i++) + { + printf("%u: IP=%s\n", i, servers[i]->ip); + for(j = 0; j < servers[i]->num_fields; j++) + printf(" %s='%s'\n", servers[i]->field[j].name, servers[i]->field[j].value); + } + free(servers); + } + return EXIT_SUCCESS; +} + +#endif /* VERSE_MS_STANDALONE */ diff --git a/extern/verse/dist/verse_ms.h b/extern/verse/dist/verse_ms.h new file mode 100644 index 00000000000..5a27d3fd446 --- /dev/null +++ b/extern/verse/dist/verse_ms.h @@ -0,0 +1,72 @@ +/* + * This is Verse Master Server, a small help library to aid application developers + * make their applications interact with a Verse master server. + * + * There are two steps to the process: + * + * 1) Send a MS:GET request to a master server. This is done by the verse_ms_get_send() + * function, which calls verse_send_ping() internally. + * + * 2) Parse any returned MS:LIST packets. This is a two-step process. The application + * still owns the ping callback, and will need to check for received pings that + * start with MS:LIST, and call the verse_ms_list_parse() function to parse those. + * + * A successfully parsed MS:LIST packet will result in an array of VMSServer pointers + * being returned. Each VMSServer instance describes one server. Use the provided + * functions to query each server structure. + * + * The application should call free() on the returned vector, whenever it is done with + * the data (perhaps after copying it into application-defined data structures). + * + * For a lot more detail about the Verse master server protocol, please see + * the spec at <http://verse.blender.org/cms/Master_Server__v2.775.0.html>. + * + * This code was written in 2006 by Emil Brink. It is released as public domain. + * +*/ + +#define VERSE_MS_VERSION "1.0" + +#if defined __cplusplus +extern "C" { +#endif + +typedef struct { + const char *name; /* Field name. Upper-case. */ + const char *value; /* Field value. Fully parsed, might contain spaces. */ +} VMSField; + +typedef struct { + const char *ip; /* IP address of server, in dotted decimal:port. */ + unsigned int num_fields; /* Number of fields of extra info. */ + VMSField *field; /* Vector of fields, or NULL if none. */ +} VMSServer; + +/* Formats and sends a MS:GET ping packet to the master server. The <fields> argument + * should be a combination of VERSE_MS_FIELD_ mask values. If <tags> is set, it should + * be a comma-separated set of include/exclude tags, like "a,b,-c,d,-e". +*/ +#define VERSE_MS_FIELD_DESCRIPTION (1 << 0) +extern void verse_ms_get_send(const char *address, int fields, const char *tags); + +/* Parses a master server response. This will be a string of the form "MS:LIST IP=blah ...", + * which is split into one struct per IP, and any additional fields parsed (unquoted etc). + * Returns an array of VMSServer pointers, which is NULL-terminated. Returns NULL if there + * was a parse error somewhere in the string, no partial success is possible. +*/ +extern VMSServer ** verse_ms_list_parse(const char *list); + +/* This is the only standard field name, currently. */ +#define VERSE_MS_FIELD_DESCRIPTION_NAME "DE" /* Human-readable server description. */ + +/* Checks wether the given server has a field with the given name. */ +extern int verse_ms_field_exists(const VMSServer *ms, const char *name); + +/* Returns the value for the named field in the given server, if present. + * If not, NULL is returned. +*/ +extern const char * verse_ms_field_value(const VMSServer *ms, const char *name); + +#if defined __cplusplus +} +#endif diff --git a/extern/verse/make/msvc_7_0/libverse.vcproj b/extern/verse/make/msvc_7_0/libverse.vcproj index ecf6208ac3d..1c9bfbfce33 100644 --- a/extern/verse/make/msvc_7_0/libverse.vcproj +++ b/extern/verse/make/msvc_7_0/libverse.vcproj @@ -229,6 +229,9 @@ ECHO Done <File RelativePath="..\..\dist\v_util.c"> </File> + <File + RelativePath="..\..\dist\verse_ms.c"> + </File> </Filter> <Filter Name="Header Files" @@ -267,6 +270,9 @@ ECHO Done RelativePath="..\..\dist\v_util.h"> </File> </Filter> + <File + RelativePath="..\..\dist\verse_ms.h"> + </File> </Files> <Globals> </Globals> |