awacke1 commited on
Commit
b704ae5
·
verified ·
1 Parent(s): 689ff91

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -6
app.py CHANGED
@@ -17,12 +17,14 @@ from gamestate import GameState
17
 
18
  # --- Constants ---
19
  SAVE_DIR = "saved_worlds"
 
20
  PLOT_WIDTH = 50.0 # Width of each plot in 3D space
21
  PLOT_DEPTH = 50.0 # Depth of each plot
22
  CSV_COLUMNS = ['obj_id', 'type', 'pos_x', 'pos_y', 'pos_z', 'rot_x', 'rot_y', 'rot_z', 'rot_order']
23
 
24
- # --- Ensure Save Directory Exists ---
25
  os.makedirs(SAVE_DIR, exist_ok=True)
 
26
 
27
  @st.cache_data(ttl=3600)
28
  def load_plot_metadata():
@@ -40,7 +42,7 @@ def load_plot_metadata():
40
  parsed_plots = []
41
  for filename in plot_files:
42
  try:
43
- parts = filename[:-4].split('_') # Remove .csv
44
  grid_x = int(parts[1][1:]) # After 'X'
45
  grid_z = int(parts[2][1:]) # After 'Z'
46
  plot_name = " ".join(parts[3:]) if len(parts) > 3 else f"Plot ({grid_x},{grid_z})"
@@ -144,6 +146,11 @@ if 'new_plot_name' not in st.session_state:
144
  st.session_state.new_plot_name = ""
145
  if 'js_save_data_result' not in st.session_state:
146
  st.session_state.js_save_data_result = None
 
 
 
 
 
147
 
148
  plots_metadata = load_plot_metadata()
149
  all_initial_objects = []
@@ -181,7 +188,7 @@ with st.sidebar:
181
  st.session_state.selected_object = selected_object_type_widget
182
 
183
  st.markdown("---")
184
- st.header("Save Work")
185
  st.caption("Saves newly placed objects to the current plot. A new plot file is created for new areas.")
186
  if st.button("💾 Save Current Work", key="save_button"):
187
  from streamlit_js_eval import streamlit_js_eval
@@ -189,7 +196,61 @@ with st.sidebar:
189
  streamlit_js_eval(js_code=js_get_data_code, key="js_save_processor")
190
  st.rerun()
191
 
192
- # --- Process Save Data from JS ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  save_data_from_js = st.session_state.get("js_save_processor", None)
194
  if save_data_from_js is not None:
195
  st.info("Received save data from client...")
@@ -238,9 +299,9 @@ if save_data_from_js is not None:
238
 
239
  # --- Main Area ---
240
  st.header("Infinite Shared 3D World")
241
- st.caption("Move to empty areas to expand the world. Use the sidebar 'Save' to store your work.")
242
 
243
- # Prepare state to inject into HTML/JS
244
  injected_state = {
245
  "ALL_INITIAL_OBJECTS": all_initial_objects,
246
  "PLOTS_METADATA": plots_metadata,
 
17
 
18
  # --- Constants ---
19
  SAVE_DIR = "saved_worlds"
20
+ GLOBAL_SAVES_DIR = "global_saves" # Folder for global game saves
21
  PLOT_WIDTH = 50.0 # Width of each plot in 3D space
22
  PLOT_DEPTH = 50.0 # Depth of each plot
23
  CSV_COLUMNS = ['obj_id', 'type', 'pos_x', 'pos_y', 'pos_z', 'rot_x', 'rot_y', 'rot_z', 'rot_order']
24
 
25
+ # --- Ensure Required Directories Exist ---
26
  os.makedirs(SAVE_DIR, exist_ok=True)
27
+ os.makedirs(GLOBAL_SAVES_DIR, exist_ok=True)
28
 
29
  @st.cache_data(ttl=3600)
30
  def load_plot_metadata():
 
42
  parsed_plots = []
43
  for filename in plot_files:
44
  try:
45
+ parts = filename[:-4].split('_') # Remove .csv extension
46
  grid_x = int(parts[1][1:]) # After 'X'
47
  grid_z = int(parts[2][1:]) # After 'Z'
48
  plot_name = " ".join(parts[3:]) if len(parts) > 3 else f"Plot ({grid_x},{grid_z})"
 
146
  st.session_state.new_plot_name = ""
147
  if 'js_save_data_result' not in st.session_state:
148
  st.session_state.js_save_data_result = None
149
+ if 'player_position' not in st.session_state:
150
+ # This can be updated from JS if needed
151
+ st.session_state.player_position = {"x": 0, "y": 0, "z": 0}
152
+ if 'loaded_global_state' not in st.session_state:
153
+ st.session_state.loaded_global_state = None
154
 
155
  plots_metadata = load_plot_metadata()
156
  all_initial_objects = []
 
188
  st.session_state.selected_object = selected_object_type_widget
189
 
190
  st.markdown("---")
191
+ st.header("Save Work (Per Plot)")
192
  st.caption("Saves newly placed objects to the current plot. A new plot file is created for new areas.")
193
  if st.button("💾 Save Current Work", key="save_button"):
194
  from streamlit_js_eval import streamlit_js_eval
 
196
  streamlit_js_eval(js_code=js_get_data_code, key="js_save_processor")
197
  st.rerun()
198
 
199
+ st.markdown("---")
200
+ st.header("Global Save & Load")
201
+ # Global Save: Persists the entire game state plus current player position.
202
+ if st.button("💾 Global Save"):
203
+ global_save_data = {
204
+ "timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
205
+ "game_state": game_state.get_state(),
206
+ "player_position": st.session_state.get("player_position", {"x": 0, "y": 0, "z": 0})
207
+ }
208
+ default_save_name = f"save_{time.strftime('%Y%m%d_%H%M%S')}.json"
209
+ save_file_path = os.path.join(GLOBAL_SAVES_DIR, default_save_name)
210
+ with open(save_file_path, "w", encoding="utf-8") as f:
211
+ json.dump(global_save_data, f, indent=2)
212
+ st.success(f"Global state saved to {default_save_name}")
213
+ # Also store it in session state for download purposes.
214
+ st.session_state.loaded_global_state = global_save_data
215
+
216
+ # List global save files available in the folder.
217
+ st.subheader("📂 Global Saves")
218
+ save_files = sorted([f for f in os.listdir(GLOBAL_SAVES_DIR) if f.endswith(".json")])
219
+ for file in save_files:
220
+ if st.button(f"Load {file}", key=file):
221
+ file_path = os.path.join(GLOBAL_SAVES_DIR, file)
222
+ with open(file_path, "r", encoding="utf-8") as f:
223
+ loaded_save = json.load(f)
224
+ # Update the shared game state with loaded data.
225
+ with game_state.lock:
226
+ game_state.world_state = loaded_save.get("game_state", [])
227
+ st.session_state.player_position = loaded_save.get("player_position", {"x": 0, "y": 0, "z": 0})
228
+ st.session_state.loaded_global_state = loaded_save
229
+ st.success(f"Global state loaded from {file}")
230
+
231
+ st.markdown("---")
232
+ st.header("Download Global Save as Markdown")
233
+ # Use the loaded global state if available; otherwise, use the last global save data.
234
+ current_save = st.session_state.get("loaded_global_state", {"timestamp": "N/A", "game_state": [], "player_position": {"x": 0, "y": 0, "z": 0}})
235
+ # Auto-generate a default markdown file name
236
+ default_md_name = current_save.get("timestamp", "save").replace(":", "").replace(" ", "_") + ".md"
237
+ download_name = st.text_input("Override File Name", value=default_md_name)
238
+ if st.button("Generate Markdown & Download"):
239
+ md_outline = f"""# Global Save: {download_name}
240
+ - ⏰ **Timestamp:** {current_save.get("timestamp", "N/A")}
241
+ - 🎮 **Number of Game Objects:** {len(current_save.get("game_state", []))}
242
+ - 🧭 **Player Position:** {current_save.get("player_position", {"x":0, "y":0, "z":0})}
243
+
244
+ ## Game Objects:
245
+ """
246
+ # Optionally, list brief info for each object.
247
+ for i, obj in enumerate(current_save.get("game_state", []), start=1):
248
+ obj_type = obj.get("type", "Unknown")
249
+ pos = (obj.get("pos_x", 0), obj.get("pos_y", 0), obj.get("pos_z", 0))
250
+ md_outline += f"- {i}. ✨ **{obj_type}** at {pos}\n"
251
+ st.download_button("Download Markdown Save", data=md_outline, file_name=download_name, mime="text/markdown")
252
+
253
+ # --- Process Save Data from JS (Per Plot Save) ---
254
  save_data_from_js = st.session_state.get("js_save_processor", None)
255
  if save_data_from_js is not None:
256
  st.info("Received save data from client...")
 
299
 
300
  # --- Main Area ---
301
  st.header("Infinite Shared 3D World")
302
+ st.caption("Move to empty areas to expand the world. Use the sidebar 'Save' controls to store your work.")
303
 
304
+ # Inject state into JS—including the shared GAME_STATE from our GameState singleton.
305
  injected_state = {
306
  "ALL_INITIAL_OBJECTS": all_initial_objects,
307
  "PLOTS_METADATA": plots_metadata,