awacke1 commited on
Commit
d93d024
·
verified ·
1 Parent(s): c269949

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -34
app.py CHANGED
@@ -25,8 +25,10 @@ def load_plot_metadata():
25
  """Scans save dir for plot_X*_Z*.csv, sorts, calculates metadata."""
26
  plots = []
27
  plot_files = []
 
28
  try:
29
- plot_files = [f for f in os.listdir(SAVE_DIR) if f.endswith(".csv") and f.startswith("plot_X")]
 
30
  except FileNotFoundError:
31
  st.error(f"Save directory '{SAVE_DIR}' not found.")
32
  return []
@@ -59,7 +61,7 @@ def load_plot_metadata():
59
 
60
  # Sort primarily by X, then by Z
61
  parsed_plots.sort(key=lambda p: (p['grid_x'], p['grid_z']))
62
-
63
  return parsed_plots
64
 
65
  def load_plot_objects(filename, x_offset, z_offset):
@@ -70,7 +72,7 @@ def load_plot_objects(filename, x_offset, z_offset):
70
  df = pd.read_csv(file_path)
71
  # Check required columns
72
  if not all(col in df.columns for col in ['type', 'pos_x', 'pos_y', 'pos_z']):
73
- st.warning(f"CSV '{filename}' missing essential columns. Skipping.")
74
  return []
75
  # Add defaults for optional columns
76
  df['obj_id'] = df.get('obj_id', pd.Series([str(uuid.uuid4()) for _ in range(len(df))]))
@@ -124,7 +126,8 @@ def save_plot_data(filename, objects_data_list, plot_x_offset, plot_z_offset):
124
  try:
125
  df = pd.DataFrame(relative_objects, columns=CSV_COLUMNS)
126
  df.to_csv(file_path, index=False)
127
- st.success(f"Saved {len(relative_objects)} objects to {filename}")
 
128
  return True
129
  except Exception as e:
130
  st.error(f"Failed to save plot data to {filename}: {e}")
@@ -135,22 +138,34 @@ st.set_page_config( page_title="Infinite World Builder", layout="wide")
135
 
136
  # --- Initialize Session State ---
137
  if 'selected_object' not in st.session_state: st.session_state.selected_object = 'None'
138
- if 'new_plot_name' not in st.session_state: st.session_state.new_plot_name = "" # No longer used for filename
139
  if 'js_save_data_result' not in st.session_state: st.session_state.js_save_data_result = None
140
 
141
  # --- Load Plot Metadata ---
142
- # This is now the source of truth for saved plots
 
143
  plots_metadata = load_plot_metadata()
144
 
145
  # --- Load ALL Objects for Rendering ---
146
  all_initial_objects = []
 
147
  for plot in plots_metadata:
 
148
  all_initial_objects.extend(load_plot_objects(plot['filename'], plot['x_offset'], plot['z_offset']))
 
 
149
 
150
  # --- Sidebar ---
151
  with st.sidebar:
152
  st.title("🏗️ World Controls")
153
 
 
 
 
 
 
 
 
154
  st.header("Navigation (Plots)")
155
  st.caption("Click to teleport player to a plot.")
156
  max_cols = 2 # Adjust columns for potentially more buttons
@@ -161,11 +176,11 @@ with st.sidebar:
161
  for plot in sorted_plots_for_nav:
162
  button_label = f"➡️ {plot.get('name', plot['id'])} ({plot['grid_x']},{plot['grid_z']})"
163
  if cols[col_idx].button(button_label, key=f"nav_{plot['id']}"):
164
- target_x = plot['x_offset']
165
- target_z = plot['z_offset'] # Use Z offset too
 
166
  try:
167
- # Tell JS where to teleport (center of plot approx)
168
- js_code = f"teleportPlayer({target_x + PLOT_WIDTH/2}, {target_z + PLOT_DEPTH/2});"
169
  streamlit_js_eval(js_code=js_code, key=f"teleport_{plot['id']}")
170
  except Exception as e:
171
  st.error(f"Failed to send teleport command: {e}")
@@ -182,7 +197,7 @@ with st.sidebar:
182
  )
183
  if selected_object_type_widget != st.session_state.selected_object:
184
  st.session_state.selected_object = selected_object_type_widget
185
- # Rerun will happen, JS reloads state via sessionStorage, Python injects new selection
186
 
187
  st.markdown("---")
188
 
@@ -191,60 +206,53 @@ with st.sidebar:
191
  st.caption("Saves newly placed objects to the plot the player is currently in. If it's a new area, a new plot file is created.")
192
  if st.button("💾 Save Current Work", key="save_button"):
193
  # Trigger JS to get data AND player position
194
- js_get_data_code = "getSaveDataAndPosition();" # JS function needs update
195
- streamlit_js_eval(js_code=js_get_data_code, key="js_save_processor")
196
  st.rerun() # Rerun to process result
197
 
198
 
199
  # --- Process Save Data ---
200
  save_data_from_js = st.session_state.get("js_save_processor", None)
201
 
202
- if save_data_from_js is not None:
203
  st.info("Received save data from client...")
204
  save_processed_successfully = False
205
  try:
206
- # Expecting { playerPosition: {x,y,z}, objectsToSave: [...] }
207
  payload = json.loads(save_data_from_js) if isinstance(save_data_from_js, str) else save_data_from_js
208
 
209
  if isinstance(payload, dict) and 'playerPosition' in payload and 'objectsToSave' in payload:
210
  player_pos = payload['playerPosition']
211
  objects_to_save = payload['objectsToSave']
212
 
213
- if isinstance(objects_to_save, list): # Allow saving empty list (clears new objects)
214
  # Determine target plot based on player position
215
  target_grid_x = math.floor(player_pos.get('x', 0.0) / PLOT_WIDTH)
216
- target_grid_z = math.floor(player_pos.get('z', 0.0) / PLOT_DEPTH) # Use Z pos too
217
 
218
  target_filename = f"plot_X{target_grid_x}_Z{target_grid_z}.csv"
219
  target_plot_x_offset = target_grid_x * PLOT_WIDTH
220
  target_plot_z_offset = target_grid_z * PLOT_DEPTH
221
 
222
  st.write(f"Attempting to save plot: {target_filename} (Player at: x={player_pos.get('x', 0):.1f}, z={player_pos.get('z', 0):.1f})")
223
-
224
- # Check if this plot already exists in metadata (for logging/future logic)
225
  is_new_plot_file = not os.path.exists(os.path.join(SAVE_DIR, target_filename))
226
 
 
227
  save_ok = save_plot_data(target_filename, objects_to_save, target_plot_x_offset, target_plot_z_offset)
228
 
229
  if save_ok:
230
- load_plot_metadata.clear() # Clear cache to force reload metadata
231
  try: # Tell JS to clear its unsaved state
232
  streamlit_js_eval(js_code="resetNewlyPlacedObjects();", key="reset_js_state")
233
  except Exception as js_e:
234
  st.warning(f"Could not reset JS state after save: {js_e}")
235
 
236
- if is_new_plot_file:
237
- st.success(f"New plot created and saved: {target_filename}")
238
- else:
239
- st.success(f"Updated existing plot: {target_filename}")
240
  save_processed_successfully = True
241
  else:
242
  st.error(f"Failed to save plot data to file: {target_filename}")
243
- else:
244
- st.error("Invalid 'objectsToSave' format received (expected list).")
245
- else:
246
- st.error("Invalid save payload structure received from client.")
247
- print("Received payload:", payload) # Log for debugging
248
 
249
  except json.JSONDecodeError:
250
  st.error("Failed to decode save data from client.")
@@ -253,30 +261,31 @@ if save_data_from_js is not None:
253
  st.error(f"Error processing save: {e}")
254
  st.exception(e)
255
 
256
- # Clear the trigger data from session state
257
  st.session_state.js_save_processor = None
258
- # Rerun after processing to reflect changes
259
  if save_processed_successfully:
260
  st.rerun()
261
 
262
 
263
  # --- Main Area ---
264
  st.header("Infinite Shared 3D World")
265
- st.caption("Move to empty areas to expand the world. Use sidebar 'Save' to save work to current plot.")
266
 
267
  # --- Load and Prepare HTML ---
268
  html_file_path = 'index.html'
269
- html_content_with_state = None
270
 
271
  try:
272
  with open(html_file_path, 'r', encoding='utf-8') as f:
273
  html_template = f.read()
274
 
275
  # --- Inject Python state into JavaScript ---
 
276
  js_injection_script = f"""
277
  <script>
278
  window.ALL_INITIAL_OBJECTS = {json.dumps(all_initial_objects)};
279
- window.PLOTS_METADATA = {json.dumps(plots_metadata)}; // Send plot info to JS
280
  window.SELECTED_OBJECT_TYPE = {json.dumps(st.session_state.selected_object)};
281
  window.PLOT_WIDTH = {json.dumps(PLOT_WIDTH)};
282
  window.PLOT_DEPTH = {json.dumps(PLOT_DEPTH)};
 
25
  """Scans save dir for plot_X*_Z*.csv, sorts, calculates metadata."""
26
  plots = []
27
  plot_files = []
28
+ st.write(f"Scanning directory: {os.path.abspath(SAVE_DIR)}") # Debug print
29
  try:
30
+ plot_files = sorted([f for f in os.listdir(SAVE_DIR) if f.endswith(".csv") and f.startswith("plot_X")])
31
+ st.write(f"Found files: {plot_files}") # Debug print
32
  except FileNotFoundError:
33
  st.error(f"Save directory '{SAVE_DIR}' not found.")
34
  return []
 
61
 
62
  # Sort primarily by X, then by Z
63
  parsed_plots.sort(key=lambda p: (p['grid_x'], p['grid_z']))
64
+ st.write(f"Loaded metadata for {len(parsed_plots)} plots.") # Debug print
65
  return parsed_plots
66
 
67
  def load_plot_objects(filename, x_offset, z_offset):
 
72
  df = pd.read_csv(file_path)
73
  # Check required columns
74
  if not all(col in df.columns for col in ['type', 'pos_x', 'pos_y', 'pos_z']):
75
+ # st.warning(f"CSV '{filename}' missing essential columns. Skipping.") # Can be noisy
76
  return []
77
  # Add defaults for optional columns
78
  df['obj_id'] = df.get('obj_id', pd.Series([str(uuid.uuid4()) for _ in range(len(df))]))
 
126
  try:
127
  df = pd.DataFrame(relative_objects, columns=CSV_COLUMNS)
128
  df.to_csv(file_path, index=False)
129
+ # Success message handled in the calling block
130
+ # st.success(f"Saved {len(relative_objects)} objects to {filename}")
131
  return True
132
  except Exception as e:
133
  st.error(f"Failed to save plot data to {filename}: {e}")
 
138
 
139
  # --- Initialize Session State ---
140
  if 'selected_object' not in st.session_state: st.session_state.selected_object = 'None'
141
+ # Removed 'new_plot_name' state as it wasn't used for saving anymore
142
  if 'js_save_data_result' not in st.session_state: st.session_state.js_save_data_result = None
143
 
144
  # --- Load Plot Metadata ---
145
+ # Cached function returns list of plots
146
+ # Note: next_plot_x_offset isn't returned/needed anymore
147
  plots_metadata = load_plot_metadata()
148
 
149
  # --- Load ALL Objects for Rendering ---
150
  all_initial_objects = []
151
+ st.write(f"Loading objects for {len(plots_metadata)} plots...") # Debug
152
  for plot in plots_metadata:
153
+ loaded_count = len(all_initial_objects)
154
  all_initial_objects.extend(load_plot_objects(plot['filename'], plot['x_offset'], plot['z_offset']))
155
+ # st.write(f" Loaded {len(all_initial_objects) - loaded_count} objects from {plot['filename']}") # Verbose Debug
156
+ st.write(f"Total objects loaded: {len(all_initial_objects)}") # Debug
157
 
158
  # --- Sidebar ---
159
  with st.sidebar:
160
  st.title("🏗️ World Controls")
161
 
162
+ # *** NEW: Refresh Button ***
163
+ if st.button("🔄 Refresh World View", key="refresh_button"):
164
+ st.info("Clearing cache and reloading world data...")
165
+ load_plot_metadata.clear() # Clear the cache for plot metadata
166
+ # No need to clear all_initial_objects, it's rebuilt after rerun
167
+ st.rerun() # Force the script to rerun from the top
168
+
169
  st.header("Navigation (Plots)")
170
  st.caption("Click to teleport player to a plot.")
171
  max_cols = 2 # Adjust columns for potentially more buttons
 
176
  for plot in sorted_plots_for_nav:
177
  button_label = f"➡️ {plot.get('name', plot['id'])} ({plot['grid_x']},{plot['grid_z']})"
178
  if cols[col_idx].button(button_label, key=f"nav_{plot['id']}"):
179
+ # Teleport near center of plot
180
+ target_x = plot['x_offset'] + PLOT_WIDTH / 2
181
+ target_z = plot['z_offset'] + PLOT_DEPTH / 2
182
  try:
183
+ js_code = f"teleportPlayer({target_x}, {target_z});"
 
184
  streamlit_js_eval(js_code=js_code, key=f"teleport_{plot['id']}")
185
  except Exception as e:
186
  st.error(f"Failed to send teleport command: {e}")
 
197
  )
198
  if selected_object_type_widget != st.session_state.selected_object:
199
  st.session_state.selected_object = selected_object_type_widget
200
+ # Selecting causes rerun, state injection handles update in JS, sessionStorage preserves placed objects
201
 
202
  st.markdown("---")
203
 
 
206
  st.caption("Saves newly placed objects to the plot the player is currently in. If it's a new area, a new plot file is created.")
207
  if st.button("💾 Save Current Work", key="save_button"):
208
  # Trigger JS to get data AND player position
209
+ js_get_data_code = "getSaveDataAndPosition();" # JS function name
210
+ streamlit_js_eval(js_code=js_get_data_code, key="js_save_processor") # Store result
211
  st.rerun() # Rerun to process result
212
 
213
 
214
  # --- Process Save Data ---
215
  save_data_from_js = st.session_state.get("js_save_processor", None)
216
 
217
+ if save_data_from_js is not None: # Process only if data is present
218
  st.info("Received save data from client...")
219
  save_processed_successfully = False
220
  try:
 
221
  payload = json.loads(save_data_from_js) if isinstance(save_data_from_js, str) else save_data_from_js
222
 
223
  if isinstance(payload, dict) and 'playerPosition' in payload and 'objectsToSave' in payload:
224
  player_pos = payload['playerPosition']
225
  objects_to_save = payload['objectsToSave']
226
 
227
+ if isinstance(objects_to_save, list): # Allow saving empty list
228
  # Determine target plot based on player position
229
  target_grid_x = math.floor(player_pos.get('x', 0.0) / PLOT_WIDTH)
230
+ target_grid_z = math.floor(player_pos.get('z', 0.0) / PLOT_DEPTH)
231
 
232
  target_filename = f"plot_X{target_grid_x}_Z{target_grid_z}.csv"
233
  target_plot_x_offset = target_grid_x * PLOT_WIDTH
234
  target_plot_z_offset = target_grid_z * PLOT_DEPTH
235
 
236
  st.write(f"Attempting to save plot: {target_filename} (Player at: x={player_pos.get('x', 0):.1f}, z={player_pos.get('z', 0):.1f})")
 
 
237
  is_new_plot_file = not os.path.exists(os.path.join(SAVE_DIR, target_filename))
238
 
239
+ # --- Save the data ---
240
  save_ok = save_plot_data(target_filename, objects_to_save, target_plot_x_offset, target_plot_z_offset)
241
 
242
  if save_ok:
243
+ load_plot_metadata.clear() # Clear cache crucial for others/refresh
244
  try: # Tell JS to clear its unsaved state
245
  streamlit_js_eval(js_code="resetNewlyPlacedObjects();", key="reset_js_state")
246
  except Exception as js_e:
247
  st.warning(f"Could not reset JS state after save: {js_e}")
248
 
249
+ if is_new_plot_file: st.success(f"New plot created and saved: {target_filename}")
250
+ else: st.success(f"Updated existing plot: {target_filename}")
 
 
251
  save_processed_successfully = True
252
  else:
253
  st.error(f"Failed to save plot data to file: {target_filename}")
254
+ else: st.error("Invalid 'objectsToSave' format (expected list).")
255
+ else: st.error("Invalid save payload structure received.")
 
 
 
256
 
257
  except json.JSONDecodeError:
258
  st.error("Failed to decode save data from client.")
 
261
  st.error(f"Error processing save: {e}")
262
  st.exception(e)
263
 
264
+ # Clear the trigger data from session state IMPORTANT!
265
  st.session_state.js_save_processor = None
266
+ # Rerun after processing save to reflect changes
267
  if save_processed_successfully:
268
  st.rerun()
269
 
270
 
271
  # --- Main Area ---
272
  st.header("Infinite Shared 3D World")
273
+ st.caption(f"Location: {os.getcwd()}. Saving to sub-directory: '{SAVE_DIR}'. Plots loaded: {len(plots_metadata)}")
274
 
275
  # --- Load and Prepare HTML ---
276
  html_file_path = 'index.html'
277
+ html_content_with_state = None # Initialize
278
 
279
  try:
280
  with open(html_file_path, 'r', encoding='utf-8') as f:
281
  html_template = f.read()
282
 
283
  # --- Inject Python state into JavaScript ---
284
+ # Send plot metadata so JS knows where existing ground is
285
  js_injection_script = f"""
286
  <script>
287
  window.ALL_INITIAL_OBJECTS = {json.dumps(all_initial_objects)};
288
+ window.PLOTS_METADATA = {json.dumps(plots_metadata)};
289
  window.SELECTED_OBJECT_TYPE = {json.dumps(st.session_state.selected_object)};
290
  window.PLOT_WIDTH = {json.dumps(PLOT_WIDTH)};
291
  window.PLOT_DEPTH = {json.dumps(PLOT_DEPTH)};