Since it appears I need some way of getting a script to execute on demand on the clients for Diaspora multiplayer, I whipped this up.
Index: code/parse/sexp.cpp
===================================================================
--- code/parse/sexp.cpp (revision 10253)
+++ code/parse/sexp.cpp (working copy)
@@ -675,6 +675,7 @@
{ "damaged-escort-priority-all", OP_DAMAGED_ESCORT_LIST_ALL, 1, MAX_COMPLETE_ESCORT_LIST, SEXP_ACTION_OPERATOR, }, // Goober5000
{ "set-support-ship", OP_SET_SUPPORT_SHIP, 6, 7, SEXP_ACTION_OPERATOR, }, // Goober5000
{ "script-eval", OP_SCRIPT_EVAL, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
+ { "multi-eval", OP_SCRIPT_EVAL_MULTI, 1, INT_MAX, SEXP_ACTION_OPERATOR, },
{ "debug", OP_DEBUG, 2, 2, SEXP_ACTION_OPERATOR, }, // Karajorma
{ "do-nothing", OP_NOP, 0, 0, SEXP_ACTION_OPERATOR, },
@@ -21267,7 +21268,7 @@
}
//WMC - This is a bit of a hack, however, it's easier than
-//coding in a whole new SCript_system function.
+//coding in a whole new Script_system function.
int sexp_script_eval(int node, int return_type)
{
int n = node;
@@ -21299,9 +21300,92 @@
if(!success)
Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
+
return r;
}
+void sexp_script_eval_multi(int node)
+{
+ char s[TOKEN_LENGTH];
+ bool success = true;
+ int sindex;
+ player *p;
+
+ strcpy_s(s, CTEXT(node));
+
+ node = CDR(node);
+
+ multi_start_callback();
+ multi_send_string(s);
+ // evalutate on all clients
+ if (node == -1) {
+ multi_send_bool(true);
+ success = Script_system.EvalString(s, NULL, NULL, s);
+ }
+ // we have to send to all clients but we need to send a list of ships so that they know if they evaluate or not
+ else {
+ multi_send_bool(false);
+
+ do {
+ p = get_player_from_ship_node(node, true);
+
+ // not a player ship so skip it
+ if (p == NULL ){
+ node = CDR(node);
+ continue;
+ }
+ else {
+ // if this is me, execute the script
+ if (p == Player) {
+ success = Script_system.EvalString(s, NULL, NULL, s);
+ }
+ // otherwise notify the clients
+ else {
+ sindex = ship_name_lookup(CTEXT(node));
+ multi_send_ship(sindex);
+ }
+ }
+
+ node = CDR(node);
+ } while (node != -1);
+ }
+
+ multi_end_callback();
+
+ if(!success) {
+ Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
+ }
+}
+
+void multi_sexp_script_eval_multi()
+{
+ int sindex;
+ char s[TOKEN_LENGTH];
+ bool sent_to_all = false;
+ bool success = true;
+
+ multi_get_string(s);
+ multi_get_bool(sent_to_all);
+
+ if (sent_to_all) {
+ success = Script_system.EvalString(s, NULL, NULL, s);
+ }
+ // go through all the ships that were sent and see if any of them match this client.
+ else {
+ while (multi_get_ship(sindex)) {
+ Assertion(sindex >= 0, "Illegal value for the ship index sent in multi_sexp_script_eval_multi()! Ship %d does not exist!", sindex);
+ if (Player->objnum == Ships[sindex].objnum) {
+ success = Script_system.EvalString(s, NULL, NULL, s);
+ }
+ }
+ }
+
+ if(!success) {
+ Warning(LOCATION, "sexp-script-eval failed to evaluate string \"%s\"; check your syntax", s);
+ }
+}
+
+
void sexp_force_glide(int node)
{
ship *shipp;
@@ -23956,6 +24040,11 @@
case OP_SCRIPT_EVAL:
sexp_val = sexp_script_eval(node, OPR_NULL);
break;
+
+ case OP_SCRIPT_EVAL_MULTI:
+ sexp_script_eval_multi(node);
+ sexp_val = SEXP_TRUE;
+ break;
case OP_CHANGE_IFF_COLOR:
sexp_change_iff_color(node);
@@ -24371,6 +24460,10 @@
multi_sexp_set_ets_values();
break;
+ case OP_SCRIPT_EVAL_MULTI:
+ multi_sexp_script_eval_multi();
+ break;
+
// bad sexp in the packet
default:
// probably just a version error where the host supports a SEXP but a client does not
@@ -24974,6 +25067,7 @@
case OP_SET_SECONDARY_WEAPON:
case OP_SET_NUM_COUNTERMEASURES:
case OP_SCRIPT_EVAL:
+ case OP_SCRIPT_EVAL_MULTI:
case OP_ENABLE_BUILTIN_MESSAGES:
case OP_DISABLE_BUILTIN_MESSAGES:
case OP_LOCK_PRIMARY_WEAPON:
@@ -26991,6 +27085,12 @@
case OP_SCRIPT_EVAL:
return OPF_STRING;
+ case OP_SCRIPT_EVAL_MULTI:
+ if (argnum == 0)
+ return OPF_STRING;
+ else
+ return OPF_SHIP;
+
case OP_CHANGE_IFF_COLOR:
if ((argnum == 0) || (argnum == 1))
return OPF_IFF;
@@ -28745,6 +28845,7 @@
case OP_DAMAGED_ESCORT_LIST_ALL:
case OP_SET_SUPPORT_SHIP:
case OP_SCRIPT_EVAL:
+ case OP_SCRIPT_EVAL_MULTI:
return CHANGE_SUBCATEGORY_OTHER;
case OP_NUM_SHIPS_IN_BATTLE:
@@ -32341,17 +32442,24 @@
},
{OP_SCRIPT_EVAL_STRING, "script-eval-string\r\n"
- "\tEvaluates script to return a string"
+ "\tEvaluates script to return a string\r\n\r\n"
"Takes 1 argument...\r\n"
"\t1:\tScript\r\n"
},
{OP_SCRIPT_EVAL, "script-eval\r\n"
- "\tEvaluates script"
+ "\tEvaluates script\r\n\r\n"
"Takes at least 1 argument...\r\n"
"\t1:\tScript to evaluate\r\n"
},
+ {OP_SCRIPT_EVAL_MULTI, "multi-eval\r\n"
+ "\tEvaluates script\r\n\r\n"
+ "Takes at least 1 argument...\r\n"
+ "\t1:\tScript to evaluate\r\n"
+ "\t(rest):\tList of players who should evaluate this script. If no player is given, all clients will execute the script\r\n"
+ },
+
{OP_FORCE_GLIDE, "force-glide\r\n"
"\tForces a given ship into glide mode, provided it is capable of gliding. Note that the player will not be able to leave glide mode on his own,, and that a ship in glide mode cannot warp out or enter autopilot."
"Takes 2 Arguments...\r\n"
Index: code/parse/sexp.h
===================================================================
--- code/parse/sexp.h (revision 10253)
+++ code/parse/sexp.h (working copy)
@@ -717,6 +717,7 @@
#define OP_COPY_VARIABLE_BETWEEN_INDEXES (0x0021 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // Goober5000
#define OP_GET_ETS_VALUE (0x0022 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // niffiwan
#define OP_SET_ETS_VALUES (0x0023 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // niffiwan
+#define OP_SCRIPT_EVAL_MULTI (0x0024 | OP_CATEGORY_CHANGE2 | OP_NONCAMPAIGN_FLAG) // Karajorma
// defined for AI goals
#define OP_AI_CHASE (0x0000 | OP_CATEGORY_AI | OP_NONCAMPAIGN_FLAG)
Thing is, I'm not certain if it's a better idea to implement multiplayer features via this sort of thing, to use lua's own networking, or to make up a Freespace specific wrapper for networking (like I did with the SEXP system) since I don't actually script myself, so I'm asking which one people think is best. It might actually be advantageous to implement this alongside a wrapper.
One feature I added is the ability to tell the SEXP to only execute scripts on certain client machines. That's actually something I'm considering making part of more SEXPs (or maybe even part of the multi_sexp.cpp code). Does anyone besides me think they'd have use for SEXPs / scripts which only execute on the clients and not on the host / other machines?
[attachment deleted by an evil time traveler]