From b5883148a5468e1cdb804658aca3bd2f29b505cf Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sun, 19 Sep 2021 11:41:37 -0400 Subject: [PATCH 1/5] Download Story as JSON/Plaintext no longer requires server --- aiserver.py | 11 +++++- static/application.js | 78 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/aiserver.py b/aiserver.py index 07b6ea4f..a4b96f55 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1600,7 +1600,7 @@ def togglememorymode(): vars.mode = "memory" emit('from_server', {'cmd': 'memmode', 'data': 'true'}, broadcast=True) emit('from_server', {'cmd': 'setinputtext', 'data': vars.memory}, broadcast=True) - emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) elif(vars.mode == "memory"): vars.mode = "play" emit('from_server', {'cmd': 'memmode', 'data': 'false'}, broadcast=True) @@ -2111,7 +2111,13 @@ def loadRequest(loadpath): vars.loadselect = "" # Refresh game screen + filename = path.basename(loadpath) + if(filename.endswith('.json')): + filename = filename[:-5] + emit('from_server', {'cmd': 'setstoryname', 'data': filename}, broadcast=True) sendwi() + emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) refresh_story() emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}, broadcast=True) @@ -2329,7 +2335,10 @@ def newGameRequest(): vars.savedir = getcwd()+"\stories" # Refresh game screen + emit('from_server', {'cmd': 'setstoryname', 'data': None}, broadcast=True) sendwi() + emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) setStartState() def randomGameRequest(topic): diff --git a/static/application.js b/static/application.js index 3a0a457f..a9a56480 100644 --- a/static/application.js +++ b/static/application.js @@ -62,7 +62,9 @@ var rs_close; var seqselmenu; var seqselcontents; +var storyname = null; var memorymode = false; +var memorytext = ""; var gamestarted = false; var editmode = false; var connected = false; @@ -184,7 +186,7 @@ function addImportLine(ob) { function addWiLine(ob) { if(ob.init) { if(ob.selective){ - wi_menu.append("
\ + wi_menu.append("
\
\ \ \ @@ -205,7 +207,7 @@ function addWiLine(ob) {
\
"); } else { - wi_menu.append("
\ + wi_menu.append("
\
\ \ \ @@ -282,18 +284,22 @@ function addWiLine(ob) { }); $("#btn_wiselon"+ob.num).on("click", function () { enableWiSelective(ob.num); + $("#wikey"+ob.num).addClass("wilistitem-selective"); }); $("#btn_wiseloff"+ob.num).on("click", function () { disableWiSelective(ob.num); + $("#wikey"+ob.num).removeClass("wilistitem-selective"); }); $("#constant-key-"+ob.num).on("click", function () { var element = $("#constant-key-"+ob.num); if(element.hasClass("constant-key-icon-enabled")) { socket.send({'cmd': 'wiconstantoff', 'data': ob.num}); - element.removeClass("constant-key-icon-enabled") + element.removeClass("constant-key-icon-enabled"); + $("#wikey"+ob.num).removeClass("wilistitem-constant"); } else { socket.send({'cmd': 'wiconstanton', 'data': ob.num}); element.addClass("constant-key-icon-enabled"); + $("#wikey"+ob.num).addClass("wilistitem-constant"); } }); } @@ -485,6 +491,9 @@ function returnWiList(ar) { function dosubmit() { var txt = input_text.val(); socket.send({'cmd': 'submit', 'actionmode': adventure ? action_mode : 0, 'data': txt}); + if(memorymode) { + memorytext = input_text.val(); + } input_text.val(""); hideMessage(); hidegenseqs(); @@ -830,6 +839,57 @@ function submitEditedChunk(event) { } } +function downloadStory(format) { + var filename_without_extension = storyname !== null ? storyname : "untitled" + + var anchor = document.createElement('a'); + + var actionlist = $("chunk"); + var actionlist_compiled = []; + for(var i = 0; i < actionlist.length; i++) { + actionlist_compiled.push(actionlist[i].innerText.replace(/\u00a0/g, " ")); + } + + if(format == "plaintext") { + var objectURL = URL.createObjectURL(new Blob(actionlist_compiled)); + anchor.setAttribute('href', objectURL); + anchor.setAttribute('download', filename_without_extension + ".txt"); + anchor.click(); + URL.revokeObjectURL(objectURL); + return; + } + + var wilist = $(".wilistitem"); + var wilist_compiled = []; + for(var i = 0; i < wilist.length-1; i++) { + var selective = wilist[i].classList.contains("wilistitem-selective"); + wilist_compiled.push({ + key: selective ? $("#wikeyprimary"+i).val() : $("#wikey"+i).val(), + keysecondary: $("#wikeysecondary"+i).val(), + content: $("#wientry"+i).val(), + selective: selective, + constant: wilist[i].classList.contains("wilistitem-constant"), + }); + } + + var prompt = actionlist_compiled.shift(); + if(prompt === undefined) { + prompt = ""; + } + var objectURL = URL.createObjectURL(new Blob([JSON.stringify({ + gamestarted: gamestarted, + prompt: prompt, + memory: memorytext, + authorsnote: $("#anoteinput").val(), + actions: actionlist_compiled, + worldinfo: wilist_compiled, + }, null, 4)])); + anchor.setAttribute('href', objectURL); + anchor.setAttribute('download', filename_without_extension + ".json"); + anchor.click(); + URL.revokeObjectURL(objectURL); +} + //=================================================================// // READY/RUNTIME //=================================================================// @@ -980,6 +1040,8 @@ $(document).ready(function(){ } else if(msg.data == "start") { setStartState(); } + } else if(msg.cmd == "setstoryname") { + storyname = msg.data; } else if(msg.cmd == "editmode") { // Enable or Disable edit mode if(msg.data == "true") { @@ -989,6 +1051,12 @@ $(document).ready(function(){ } } else if(msg.cmd == "setinputtext") { // Set input box text for memory mode + if(memorymode) { + memorytext = msg.data; + input_text.val(msg.data); + } + } else if(msg.cmd == "setmemory") { + memorytext = msg.data; if(memorymode) { input_text.val(msg.data); } @@ -1276,11 +1344,11 @@ $(document).ready(function(){ }); button_download.on("click", function(ev) { - window.open("/download", "_blank"); + downloadStory('json') }); button_downloadtxt.on("click", function(ev) { - window.open("/download?format=plaintext", "_blank"); + downloadStory('plaintext') }); button_load.on("click", function(ev) { From 42ecd6d2d180eaf7e6a192e36fc32e320060eabb Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sun, 19 Sep 2021 11:59:31 -0400 Subject: [PATCH 2/5] Fix missing semicolon --- static/application.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/application.js b/static/application.js index a9a56480..98bb0f55 100644 --- a/static/application.js +++ b/static/application.js @@ -840,7 +840,7 @@ function submitEditedChunk(event) { } function downloadStory(format) { - var filename_without_extension = storyname !== null ? storyname : "untitled" + var filename_without_extension = storyname !== null ? storyname : "untitled"; var anchor = document.createElement('a'); From da03360e923afaf617b934d9075ae13a9d87ae48 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sun, 19 Sep 2021 14:46:30 -0400 Subject: [PATCH 3/5] Fix filename/memory/AN not syncing when downloading in some cases --- aiserver.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/aiserver.py b/aiserver.py index a4b96f55..3e6e27d6 100644 --- a/aiserver.py +++ b/aiserver.py @@ -112,6 +112,7 @@ class vars: bmsupported = False # Whether the breakmodel option is supported (GPT-Neo/GPT-J only, currently) smandelete = False # Whether stories can be deleted from inside the browser smanrename = False # Whether stories can be renamed from inside the browser + laststory = None # Filename (without extension) of most recent story JSON file we loaded acregex_ai = re.compile(r'\n* *>(.|\n)*') # Pattern for matching adventure actions from the AI so we can remove them acregex_ui = re.compile(r'^ *(>.*)$', re.MULTILINE) # Pattern for matching actions in the HTML-escaped story so we can apply colouring, etc (make sure to encase part to format in parentheses) actionmode = 1 @@ -572,14 +573,21 @@ def do_connect(): setStartState() sendsettings() refresh_settings() + vars.laststory = None + emit('from_server', {'cmd': 'setstoryname', 'data': vars.laststory}, broadcast=True) sendwi() + emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) vars.mode = "play" else: # Game in session, send current game data and ready state to browser refresh_story() sendsettings() refresh_settings() + emit('from_server', {'cmd': 'setstoryname', 'data': vars.laststory}, broadcast=True) sendwi() + emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) if(vars.mode == "play"): if(not vars.aibusy): emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) @@ -2114,7 +2122,8 @@ def loadRequest(loadpath): filename = path.basename(loadpath) if(filename.endswith('.json')): filename = filename[:-5] - emit('from_server', {'cmd': 'setstoryname', 'data': filename}, broadcast=True) + vars.laststory = filename + emit('from_server', {'cmd': 'setstoryname', 'data': vars.laststory}, broadcast=True) sendwi() emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) @@ -2234,7 +2243,11 @@ def importgame(): vars.savedir = getcwd()+"\stories" # Refresh game screen + vars.laststory = None + emit('from_server', {'cmd': 'setstoryname', 'data': vars.laststory}, broadcast=True) sendwi() + emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) refresh_story() emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) emit('from_server', {'cmd': 'hidegenseqs', 'data': ''}, broadcast=True) @@ -2278,7 +2291,11 @@ def importAidgRequest(id): vars.savedir = getcwd()+"\stories" # Refresh game screen + vars.laststory = None + emit('from_server', {'cmd': 'setstoryname', 'data': vars.laststory}, broadcast=True) sendwi() + emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) + emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) refresh_story() emit('from_server', {'cmd': 'setgamestate', 'data': 'ready'}, broadcast=True) @@ -2335,7 +2352,8 @@ def newGameRequest(): vars.savedir = getcwd()+"\stories" # Refresh game screen - emit('from_server', {'cmd': 'setstoryname', 'data': None}, broadcast=True) + vars.laststory = None + emit('from_server', {'cmd': 'setstoryname', 'data': vars.laststory}, broadcast=True) sendwi() emit('from_server', {'cmd': 'setmemory', 'data': vars.memory}, broadcast=True) emit('from_server', {'cmd': 'setanote', 'data': vars.authornote}, broadcast=True) From 99d2ce68872ac01199ac8b26bb507a42c55e21bc Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sun, 19 Sep 2021 17:00:14 -0400 Subject: [PATCH 4/5] Don't broadcast getanote and requestwiitem This prevents duplicate submissions when multiple people are connected to the same server and one person submits changes to memory, author's note or world info, by pressing Submit (for author's note or memory) or Accept (for world info). --- aiserver.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/aiserver.py b/aiserver.py index 3e6e27d6..372fae9b 100644 --- a/aiserver.py +++ b/aiserver.py @@ -1665,7 +1665,7 @@ def requestwi(): list = [] for wi in vars.worldinfo: list.append(wi["num"]) - emit('from_server', {'cmd': 'requestwiitem', 'data': list}, broadcast=True) + emit('from_server', {'cmd': 'requestwiitem', 'data': list}) #==================================================================# # Renumber WI items consecutively @@ -1791,7 +1791,7 @@ def memsubmit(data): emit('from_server', {'cmd': 'memmode', 'data': 'false'}, broadcast=True) # Ask for contents of Author's Note field - emit('from_server', {'cmd': 'getanote', 'data': ''}, broadcast=True) + emit('from_server', {'cmd': 'getanote', 'data': ''}) #==================================================================# # Commit changes to Author's Note From dff5a4e7540a870334b4d0152ba5522aad5fe0b4 Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sun, 19 Sep 2021 17:16:01 -0400 Subject: [PATCH 5/5] More missing semicolons... --- static/application.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/application.js b/static/application.js index 98bb0f55..d38b7a7d 100644 --- a/static/application.js +++ b/static/application.js @@ -1344,11 +1344,11 @@ $(document).ready(function(){ }); button_download.on("click", function(ev) { - downloadStory('json') + downloadStory('json'); }); button_downloadtxt.on("click", function(ev) { - downloadStory('plaintext') + downloadStory('plaintext'); }); button_load.on("click", function(ev) {