diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index 860521ab..672c94b5 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -920,7 +920,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) int ys = getint(p); int a1 = getint(p); int a2 = getint(p); - if(!OUTBORD(x,y) && xs > 0 && ys > 0 && !OUTBORD(x + xs-1, y + ys - 1)) + if(m_coop && !OUTBORD(x,y) && xs > 0 && ys > 0 && !OUTBORD(x + xs-1, y + ys - 1)) { block b = { x, y, xs, ys }; switch(cmd) @@ -948,7 +948,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) int ys = getint(p); int a1 = getint(p); loopi(50) av[i] = getint(p); - if(!OUTBORD(x,y) && xs > 0 && ys > 0 && !OUTBORD(x + xs-1, y + ys - 1)) + if(m_coop && !OUTBORD(x,y) && xs > 0 && ys > 0 && !OUTBORD(x + xs-1, y + ys - 1)) { block b = { x, y, xs, ys }; archxy(a1, av, b); @@ -956,9 +956,19 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) break; } + case SV_EDITBLOCK: + { + int bx = getuint(p), by = getuint(p), bxs = getuint(p), bys = getuint(p), light = getuint(p); + ucharbuf *pp = getgzbuf(p); + if(m_coop) netblockpastexy(pp, bx, by, bxs, bys, light); + freegzbuf(pp); + break; + } + case SV_NEWMAP: { int size = getint(p); + if(!m_coop) break; if(size < 0 && sfactor > 9) break; // don't enlarge over 10 in MP empty_world(size, true); if(d && d!=player1) @@ -968,6 +978,11 @@ void parsemessages(int cn, playerent *d, ucharbuf &p, bool demo = false) case SV_EDITENT: // coop edit of ent { + if(!m_coop) + { + loopi(12) getint(p); + break; + } uint i = getint(p); while((uint)ents.length()<=i) ents.add().type = NOTUSED; int to = ents[i].type; diff --git a/source/src/editing.cpp b/source/src/editing.cpp index da75b542..5cecd829 100644 --- a/source/src/editing.cpp +++ b/source/src/editing.cpp @@ -421,15 +421,52 @@ void gotoposition(char *x, char *y, char *z, char *yaw, char *pitch) } COMMAND(gotoposition, "sssss"); +const int MAXNETBLOCKSQR = MAXGZMSGSIZE / sizeof(sqr); + +void netblockpaste(const block &b, int bx, int by, bool light) // transmit block to be pasted at bx,by +{ + ASSERT(b.xs * b.ys <= MAXNETBLOCKSQR); + vector t, tmp; + putuint(t, bx); + putuint(t, by); + putuint(t, b.xs); + putuint(t, b.ys); + putuint(t, light ? 1 : 0); + tmp.put((uchar *)((&b)+1), b.xs * b.ys * (int)sizeof(sqr)); + putgzbuf(t, tmp); + packetbuf p(t.length() + 10, ENET_PACKET_FLAG_RELIABLE); + putint(p, SV_EDITBLOCK); + p.put(t.getbuf(), t.length()); + sendpackettoserv(1, p.finalize()); +} + +void netblockpastexy(ucharbuf *p, int bx, int by, int bxs, int bys, int light) +{ + if(bxs < 0 || bys < 0 || bxs > ssize || bys > ssize || bxs * bys > MAXNETBLOCKSQR || OUTBORD(bx, by) || OUTBORD(bx+bxs-1, by+bys-1)) return; + block *b = (block *)new uchar[sizeof(block) + bxs * bys * sizeof(sqr)]; + b->x = bx; b->y = by; b->xs = bxs; b->ys = bys; + memcpy(b + 1, p->buf, p->maxlen); + makeundo(*b); + blockpaste(*b, bx, by, light != 0, NULL); + freeblockp(b); +} + void editundo() { - EDITMP("undo"); + EDIT("undo"); + bool mp = multiplayer(NULL); + if(mp && undos.length() && undos.last()->xs * undos.last()->ys > MAXNETBLOCKSQR) + { + conoutf("\f3next undo area too big for multiplayer editing"); + return; + } if(undos.empty()) { conoutf("nothing more to undo"); return; } block *p = undos.pop(); undolevel--; redos.add(blockcopy(*p)); if(editmetakeydown) restoreposition(*p); blockpaste(*p); + if(mp) netblockpaste(*p, p->x, p->y, true); freeblock(p); unsavededits++; } @@ -449,13 +486,20 @@ COMMANDN(undolevel, gotoundolevel, "s"); void editredo() { - EDITMP("redo"); + EDIT("redo"); + bool mp = multiplayer(NULL); + if(mp && redos.length() && redos.last()->xs * redos.last()->ys > MAXNETBLOCKSQR) + { + conoutf("\f3next redo area too big for multiplayer editing"); + return; + } if(redos.empty()) { conoutf("nothing more to redo"); return; } block *p = redos.pop(); undos.add(blockcopy(*p)); undolevel++; if(editmetakeydown) restoreposition(*p); blockpaste(*p); + if(mp) netblockpaste(*p, p->x, p->y, true); freeblock(p); unsavededits++; } @@ -561,7 +605,7 @@ void resetcopybuffers() void copy() { - EDITSELMP("copy"); + EDITSEL("copy"); resetcopybuffers(); copytexconfig = texconfig_copy(); @@ -576,12 +620,24 @@ COMMAND(copy, ""); void paste() { - EDITSELMP("paste"); + EDITSEL("paste"); + bool mp = multiplayer(NULL); + if(mp) + { + loopv(copybuffers) if(copybuffers[i]->xs * copybuffers[i]->ys > MAXNETBLOCKSQR) + { + conoutf("\f3copied area too big for multiplayer pasting"); + return; + } + } if(!copybuffers.length()) { conoutf("nothing to paste"); return; } uchar usedslots[256] = { 0 }, *texmap = NULL; - loopv(copybuffers) blocktexusage(*copybuffers[i], usedslots); - if(sels.length() && copytexconfig) texmap = texconfig_paste(copytexconfig, usedslots); + if(!mp) + { + loopv(copybuffers) blocktexusage(*copybuffers[i], usedslots); + if(sels.length() && copytexconfig) texmap = texconfig_paste(copytexconfig, usedslots); + } loopv(sels) { @@ -601,6 +657,7 @@ void paste() if(!correctsel(sel) || sel.xs!=copyblock->xs || sel.ys!=copyblock->ys) { conoutf("incorrect selection"); return; } makeundo(sel); blockpaste(*copyblock, sel.x, sel.y, true, texmap); + if(mp) netblockpaste(*copyblock, sel.x, sel.y, true); } } } @@ -1141,7 +1198,8 @@ COMMAND(selectionflip, "s"); void selectionwalk(char *action, char *beginsel, char *endsel) { - EDITSELMP("selectionwalk"); + EDITSEL("selectionwalk"); + bool mp = multiplayer(NULL); if(*action) { const char *localvars[] = { "sw_cursel", "sw_abs_x", "sw_abs_y", "sw_rel_x", "sw_rel_y", "sw_type", "sw_floor", "sw_ceil", "sw_wtex", "sw_ftex", "sw_ctex", "sw_utex", "sw_vdelta", "sw_tag", "sw_r", "sw_g", "sw_b", "" }; @@ -1154,7 +1212,7 @@ void selectionwalk(char *action, char *beginsel, char *endsel) defformatstring(tmp)("%d %d %d %d", sel.x, sel.y, sel.xs, sel.ys); alias("sw_cursel", tmp); if(*beginsel) execute(beginsel); - bool haveundo = false; + bool haveundo = false, isro = mp && sel.xs * sel.ys > MAXNETBLOCKSQR, didwarn = false; loop(x, sel.xs) loop(y, sel.ys) { sqr *s = S(sel.x + x, sel.y + y), so = *s; @@ -1172,12 +1230,29 @@ void selectionwalk(char *action, char *beginsel, char *endsel) #undef GETA if(memcmp(s, &so, sizeof(sqr))) { - if(!haveundo) makeundo(sel); - haveundo = true; - *s = so; + if(isro) + { + if(!didwarn) conoutf("\f3selectionwalk: selected area %dx%d too big for multiplayer editing, changes ignored!", sel.xs, sel.ys); + didwarn = true; + } + else + { + if(!haveundo) makeundo(sel); + haveundo = true; + *s = so; + } } } - if(haveundo) remip(sel); + if(haveundo) + { + if(mp) + { + block *b = blockcopy(sel); + netblockpaste(*b, sel.x, sel.y, true); + freeblockp(b); + } + remip(sel); + } if(*endsel) execute(endsel); } for(int i = 0; *localvars[i]; i++) pop(localvars[i]); diff --git a/source/src/protocol.cpp b/source/src/protocol.cpp index 738697d5..4af51532 100644 --- a/source/src/protocol.cpp +++ b/source/src/protocol.cpp @@ -399,7 +399,7 @@ static const int msgsizes[] = // size inclusive message token, 0 f SV_DISCSCORES, 0, SV_TIMEUP, 3, SV_EDITENT, 13, SV_ITEMACC, 2, SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 2, SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2, - SV_EDITMODE, 2, SV_EDITXY, 8, SV_EDITARCH, 56, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, SV_NEWMAP, 2, + SV_EDITMODE, 2, SV_EDITXY, 8, SV_EDITARCH, 56, SV_EDITBLOCK, 0, SV_EDITD, 6, SV_EDITE, 6, SV_NEWMAP, 2, SV_SENDMAP, 0, SV_RECVMAP, 1, SV_REMOVEMAP, 0, SV_SERVMSG, 0, SV_ITEMLIST, 0, SV_WEAPCHANGE, 2, SV_PRIMARYWEAP, 2, SV_FLAGACTION, 3, SV_FLAGINFO, 0, SV_FLAGMSG, 0, SV_FLAGCNT, 3, diff --git a/source/src/protocol.h b/source/src/protocol.h index c0377893..3e3fc910 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -30,7 +30,7 @@ enum SV_DISCSCORES, SV_TIMEUP, SV_EDITENT, SV_ITEMACC, SV_MAPCHANGE, SV_ITEMSPAWN, SV_ITEMPICKUP, SV_PING, SV_PONG, SV_CLIENTPING, SV_GAMEMODE, - SV_EDITMODE, SV_EDITXY, SV_EDITARCH, SV_EDITS, SV_EDITD, SV_EDITE, SV_NEWMAP, + SV_EDITMODE, SV_EDITXY, SV_EDITARCH, SV_EDITBLOCK, SV_EDITD, SV_EDITE, SV_NEWMAP, SV_SENDMAP, SV_RECVMAP, SV_REMOVEMAP, SV_SERVMSG, SV_ITEMLIST, SV_WEAPCHANGE, SV_PRIMARYWEAP, SV_FLAGACTION, SV_FLAGINFO, SV_FLAGMSG, SV_FLAGCNT, diff --git a/source/src/protos.h b/source/src/protos.h index bce6b2e5..1cb7d9f2 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -716,6 +716,7 @@ extern void text_endcolumns(); #define EDITSEL(x) if(noteditmode(x) || noselection()) return #define EDITSELMP(x) if(noteditmode(x) || noselection() || multiplayer(x)) return #define EDITMP(x) if(noteditmode(x) || multiplayer(x)) return +#define EDIT(x) if(noteditmode(x)) return extern void cursorupdate(); extern void toggleedit(bool force = false); extern void reseteditor(); @@ -723,6 +724,7 @@ extern char *editinfo(); extern void makeundo(block &sel); extern void editdrag(bool isdown); extern void checkselections(); +extern void netblockpastexy(ucharbuf *p, int bx, int by, int bxs, int bys, int light); extern void setvdeltaxy(int delta, block &sel); extern void editequalisexy(bool isfloor, block &sel); extern void edittypexy(int type, block &sel); diff --git a/source/src/server.cpp b/source/src/server.cpp index 373d39ff..168d9995 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -2743,7 +2743,7 @@ int checktype(int type, client *cl) SV_SENDDEMOLIST, SV_SENDDEMO, SV_DEMOPLAYBACK, SV_CLIENT, SV_HUDEXTRAS, SV_POINTS }; // only allow edit messages in coop-edit mode - static int edittypes[] = { SV_EDITENT, SV_EDITXY, SV_EDITARCH, SV_EDITS, SV_EDITD, SV_EDITE, SV_NEWMAP }; + static int edittypes[] = { SV_EDITENT, SV_EDITXY, SV_EDITARCH, SV_EDITBLOCK, SV_EDITD, SV_EDITE, SV_NEWMAP }; if(cl) { loopi(sizeof(servtypes)/sizeof(int)) if(type == servtypes[i]) return -1; @@ -3725,6 +3725,14 @@ void process(ENetPacket *packet, int sender, int chan) break; } + case SV_EDITBLOCK: + { + loopi(5) getuint(p); + freegzbuf(getgzbuf(p)); + QUEUE_MSG; + break; + } + case -1: disconnect_client(sender, DISC_TAGT); return; diff --git a/source/src/server.h b/source/src/server.h index 5c159860..5bc76f62 100644 --- a/source/src/server.h +++ b/source/src/server.h @@ -426,7 +426,7 @@ const char *messagenames[SV_NUM] = "SV_DISCSCORES", "SV_TIMEUP", "SV_EDITENT", "SV_ITEMACC", "SV_MAPCHANGE", "SV_ITEMSPAWN", "SV_ITEMPICKUP", "SV_PING", "SV_PONG", "SV_CLIENTPING", "SV_GAMEMODE", - "SV_EDITMODE", "SV_EDITXY", "SV_EDITARCH", "SV_EDITS", "SV_EDITD", "SV_EDITE", "SV_NEWMAP", + "SV_EDITMODE", "SV_EDITXY", "SV_EDITARCH", "SV_EDITBLOCK", "SV_EDITD", "SV_EDITE", "SV_NEWMAP", "SV_SENDMAP", "SV_RECVMAP", "SV_REMOVEMAP", "SV_SERVMSG", "SV_ITEMLIST", "SV_WEAPCHANGE", "SV_PRIMARYWEAP", "SV_FLAGACTION", "SV_FLAGINFO", "SV_FLAGMSG", "SV_FLAGCNT",