Files
chrani-bot-tng/bot/modules/locations/widgets/manage_locations_widget.py

1042 lines
42 KiB
Python
Raw Permalink Normal View History

2025-11-21 07:26:02 +01:00
from bot import loaded_modules_dict
from os import path, pardir
import json
module_name = path.basename(path.normpath(path.join(path.abspath(__file__), pardir, pardir)))
widget_name = path.basename(path.abspath(__file__))[:-3]
# View Registry - defines all available views and their navigation labels
# This eliminates the need for separate template files for each button
VIEW_REGISTRY = {
'frontend': {
'label_active': 'back',
'label_inactive': 'main',
'action': 'show_frontend',
'include_in_menu': False
},
'options': {
'label_active': 'back',
'label_inactive': 'options',
'action': 'show_options',
'include_in_menu': True
},
'map': {
'label_active': 'hide map',
'label_inactive': 'show map',
'action': 'show_map',
'include_in_menu': True
},
'create_new': {
'label_active': 'back',
'label_inactive': 'create new',
'action': 'show_create_new',
'include_in_menu': True
},
'special_locations': {
'label_active': 'back',
'label_inactive': 'special',
'action': 'show_special_locations',
'include_in_menu': True
}
}
def get_table_row_css_class(location_dict):
is_enabled = location_dict.get("is_enabled", False)
if is_enabled:
css_class = "is_enabled"
else:
css_class = ""
return css_class
def select_view(*args, **kwargs):
module = args[0]
dispatchers_steamid = kwargs.get('dispatchers_steamid', None)
current_view = module.get_current_view(dispatchers_steamid)
if current_view == "options":
options_view(module, dispatchers_steamid=dispatchers_steamid, current_view=current_view)
elif current_view == "map":
map_view(module, dispatchers_steamid=dispatchers_steamid, current_view=current_view)
elif current_view == "special_locations":
frontend_view(module, dispatchers_steamid=dispatchers_steamid, current_view=current_view)
elif current_view == "delete-modal":
frontend_view(module, dispatchers_steamid=dispatchers_steamid)
delete_modal_view(module, dispatchers_steamid=dispatchers_steamid)
elif current_view == "create_new":
edit_view(module, dispatchers_steamid=dispatchers_steamid, current_view=current_view)
elif current_view == "edit_location_entry":
location_owner = (
module.dom.data
.get(module.get_module_identifier(), {})
.get("visibility", {})
.get(dispatchers_steamid, {})
.get("location_owner", None)
)
location_identifier = (
module.dom.data
.get(module.get_module_identifier(), {})
.get("visibility", {})
.get(dispatchers_steamid, {})
.get("location_identifier", None)
)
location_origin = (
module.dom.data
.get(module.get_module_identifier(), {})
.get("visibility", {})
.get(dispatchers_steamid, {})
.get("location_origin", None)
)
edit_view(
module,
dispatchers_steamid=dispatchers_steamid,
location_owner=location_owner,
location_identifier=location_identifier,
location_origin=location_origin,
current_view=current_view
)
else:
frontend_view(module, dispatchers_steamid=dispatchers_steamid)
def delete_modal_view(*args, **kwargs):
module = args[0]
dispatchers_steamid = kwargs.get('dispatchers_steamid', None)
all_available_locations = module.dom.data.get(module.get_module_identifier(), {}).get("elements", {})
all_selected_elements_count = 0
active_dataset = module.dom.data.get("module_game_environment", {}).get("active_dataset", None)
for map_identifier, location_owner in all_available_locations.items():
if active_dataset == map_identifier:
for player_steamid, player_locations in location_owner.items():
for identifier, location_dict in player_locations.items():
location_is_selected_by = location_dict.get("selected_by", [])
if dispatchers_steamid in location_is_selected_by:
all_selected_elements_count += 1
modal_confirm_delete = module.dom_management.get_delete_confirm_modal(
module,
count=all_selected_elements_count,
target_module="module_locations",
dom_element_id="location_table_modal_action_delete_button",
dom_action="delete_selected_dom_elements",
dom_element_root=module.dom_element_root,
dom_element_select_root=module.dom_element_select_root,
confirmed="True"
)
data_to_emit = modal_confirm_delete
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="modal_content",
clients=[dispatchers_steamid],
target_element={
"id": "manage_locations_widget_modal",
"type": "div",
"selector": "body > main > div"
}
)
def frontend_view(*args, **kwargs):
module = args[0]
dispatchers_steamid = kwargs.get("dispatchers_steamid", None)
current_view = kwargs.get("current_view", None)
# Load templates
template_frontend = module.templates.get_template('manage_locations_widget/view_frontend.html')
template_view_menu = module.templates.get_template('manage_locations_widget/control_view_menu.html')
control_player_location_view_template = module.templates.get_template(
'manage_locations_widget/control_player_location.html'
)
control_edit_link = module.templates.get_template('manage_locations_widget/control_edit_link.html')
control_enabled_link = module.templates.get_template('manage_locations_widget/control_enabled_link.html')
template_table_header = module.templates.get_template('manage_locations_widget/table_header.html')
template_table_rows = module.templates.get_template('manage_locations_widget/table_row.html')
template_table_footer = module.templates.get_template('manage_locations_widget/table_footer.html')
# Build table rows efficiently using list + join (avoids O(n²) with string +=)
table_rows_list = []
all_available_locations = module.dom.data.get(module.get_module_identifier(), {}).get("elements", {})
all_selected_elements_count = 0
active_dataset = module.dom.data.get("module_game_environment", {}).get("active_dataset", None)
for map_identifier, location_owner in all_available_locations.items():
if active_dataset == map_identifier:
for player_steamid, player_locations in location_owner.items():
player_dict = (
module.dom.data
.get("module_players", {})
.get("elements", {})
.get(active_dataset, {})
.get(player_steamid, {})
)
for identifier, location_dict in player_locations.items():
location_has_special_properties = len(location_dict.get("type", [])) >= 1
if not location_has_special_properties and current_view == "special_locations":
continue
location_is_selected_by = location_dict.get("selected_by", [])
location_entry_selected = False
if dispatchers_steamid in location_is_selected_by:
location_entry_selected = True
all_selected_elements_count += 1
# Sanitize dataset for HTML ID (replace spaces with underscores, lowercase)
location_dict_for_template = location_dict.copy()
location_dict_for_template["dataset"] = module.dom_management.sanitize_for_html_id(location_dict.get("dataset", ""))
location_dict_for_template["dataset_original"] = location_dict.get("dataset", "")
control_select_link = module.dom_management.get_selection_dom_element(
module,
target_module="module_locations",
dom_element_select_root=[identifier, "selected_by"],
dom_element=location_dict_for_template,
dom_element_entry_selected=location_entry_selected,
dom_action_inactive="select_dom_element",
dom_action_active="deselect_dom_element"
)
table_rows_list.append(module.template_render_hook(
module,
template=template_table_rows,
location=location_dict_for_template,
player_dict=player_dict,
control_select_link=control_select_link,
control_enabled_link=module.template_render_hook(
module,
template=control_enabled_link,
location=location_dict_for_template,
),
control_edit_link=module.template_render_hook(
module,
template=control_edit_link,
dispatchers_steamid=dispatchers_steamid,
location=location_dict_for_template,
)
))
table_rows = ''.join(table_rows_list)
dom_element_delete_button = module.dom_management.get_delete_button_dom_element(
module,
count=all_selected_elements_count,
target_module="module_locations",
dom_element_root=module.dom_element_root,
dom_element_select_root=module.dom_element_select_root,
dom_element_id="manage_locations_control_action_delete_link",
dom_action="delete_selected_dom_elements"
)
# Get current view and player coordinates
current_view = module.get_current_view(dispatchers_steamid)
player_coordinates = module.dom.data.get("module_players", {}).get("players", {}).get(dispatchers_steamid, {}).get(
"pos", {"x": 0, "y": 0, "z": 0}
)
# Render navigation menu using view registry
options_toggle = module.template_render_hook(
module,
template=template_view_menu,
views=VIEW_REGISTRY,
current_view=current_view,
steamid=dispatchers_steamid,
control_player_location_view=module.template_render_hook(
module,
template=control_player_location_view_template,
pos_x=player_coordinates["x"],
pos_y=player_coordinates["y"],
pos_z=player_coordinates["z"]
)
)
data_to_emit = module.template_render_hook(
module,
template=template_frontend,
options_toggle=options_toggle,
table_header=module.template_render_hook(
module,
template=template_table_header
),
table_rows=table_rows,
table_footer=module.template_render_hook(
module,
template=template_table_footer,
action_delete_button=dom_element_delete_button
)
)
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="widget_content",
clients=[dispatchers_steamid],
target_element={
"id": "manage_locations_widget",
"type": "table",
"selector": "body > main > div"
}
)
def map_view(*args, **kwargs):
module = args[0]
dispatchers_steamid = kwargs.get("dispatchers_steamid", None)
current_view = kwargs.get("current_view", None)
# Load templates
template_map = module.templates.get_template('manage_locations_widget/view_map.html')
template_view_menu = module.templates.get_template('manage_locations_widget/control_view_menu.html')
control_player_location_view_template = module.templates.get_template(
'manage_locations_widget/control_player_location.html'
)
# Get current view and player coordinates
current_view = module.get_current_view(dispatchers_steamid)
player_coordinates = module.dom.data.get("module_players", {}).get("players", {}).get(dispatchers_steamid, {}).get(
"pos", {"x": 0, "y": 0, "z": 0}
)
# Collect all locations for the map
all_locations = module.dom.data.get(module.get_module_identifier(), {}).get("elements", {})
locations_for_map = {}
active_dataset = module.dom.data.get("module_game_environment", {}).get("active_dataset", None)
for map_identifier, location_owners in all_locations.items():
if active_dataset and map_identifier != active_dataset:
continue
for owner_steamid, player_locations in location_owners.items():
for identifier, location_dict in player_locations.items():
location_id = f"{map_identifier}_{owner_steamid}_{identifier}"
coordinates = location_dict.get("coordinates", {})
dimensions = location_dict.get("dimensions", {})
shape = location_dict.get("shape", "circle")
locations_for_map[location_id] = {
"name": location_dict.get("name", "Unknown"),
"identifier": identifier,
"owner": owner_steamid,
"shape": shape,
"coordinates": {
"x": float(coordinates.get("x", 0)),
"y": float(coordinates.get("y", 0)),
"z": float(coordinates.get("z", 0))
},
"dimensions": dimensions,
"teleport_entry": location_dict.get("teleport_entry", {}),
"type": location_dict.get("type", []),
"is_enabled": location_dict.get("is_enabled", False)
}
# Collect all online players for the map
players_module = loaded_modules_dict.get("module_players")
players_for_map = {}
if players_module:
active_dataset = module.dom.data.get("module_game_environment", {}).get("active_dataset", None)
all_players = players_module.dom.data.get("module_players", {}).get("elements", {})
if active_dataset and active_dataset in all_players:
for steamid, player_dict in all_players[active_dataset].items():
if player_dict.get("is_online", False):
players_for_map[steamid] = {
"name": player_dict.get("name", "Player"),
"level": player_dict.get("level", 0),
"health": player_dict.get("health", 0),
"zombies": player_dict.get("zombies", 0),
"players": player_dict.get("players", 0),
"deaths": player_dict.get("deaths", 0),
"score": player_dict.get("score", 0),
"ping": player_dict.get("ping", 0),
"is_authenticated": player_dict.get("is_authenticated", False),
"is_muted": player_dict.get("is_muted", False),
"is_initialized": player_dict.get("is_initialized", False),
"in_limbo": player_dict.get("in_limbo", False),
"permission_level": player_dict.get("permission_level", None),
"dataset": active_dataset,
"pos": {
"x": player_dict.get("pos", {}).get("x", 0),
"y": player_dict.get("pos", {}).get("y", 0),
"z": player_dict.get("pos", {}).get("z", 0)
}
}
# Get gameprefs for map legend
gameprefs = {}
if active_dataset:
gameprefs = (
module.dom.data
.get("module_game_environment", {})
.get(active_dataset, {})
.get("gameprefs", {})
)
# Load webmap templates from players and locations modules
webmap_templates = {}
# Load player webmap templates
if players_module:
try:
webmap_templates['player_popup'] = players_module.templates.get_template('webmap/player_popup.html').render()
webmap_templates['player_markers'] = players_module.templates.get_template('webmap/player_markers.html').render()
webmap_templates['player_update_handler'] = players_module.templates.get_template('webmap/player_update_handler.html').render()
webmap_templates['player_actions'] = players_module.templates.get_template('webmap/player_actions.html').render()
except Exception as e:
module.logger.error(f"Error loading player webmap templates: {e}")
# Load location webmap templates
try:
webmap_templates['location_shapes'] = module.templates.get_template('webmap/location_shapes.html').render()
webmap_templates['location_update_handler'] = module.templates.get_template('webmap/location_update_handler.html').render()
webmap_templates['location_actions'] = module.templates.get_template('webmap/location_actions.html').render()
except Exception as e:
module.logger.error(f"Error loading location webmap templates: {e}")
# Render navigation menu using view registry
control_switch_view = module.template_render_hook(
module,
template=template_view_menu,
views=VIEW_REGISTRY,
current_view=current_view,
steamid=dispatchers_steamid,
control_player_location_view=module.template_render_hook(
module,
template=control_player_location_view_template,
pos_x=player_coordinates["x"],
pos_y=player_coordinates["y"],
pos_z=player_coordinates["z"]
)
)
data_to_emit = module.template_render_hook(
module,
template=template_map,
control_switch_view=control_switch_view,
webmap_templates=webmap_templates
)
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="widget_content",
clients=[dispatchers_steamid],
target_element={
"id": "manage_locations_widget",
"type": "table",
"selector": "body > main > div"
}
)
# Send map metadata via Socket.IO
module.webserver.send_data_to_client_hook(
module,
payload={
"gameprefs": gameprefs,
"active_dataset": active_dataset or "Unknown"
},
data_type="map_metadata",
clients=[dispatchers_steamid]
)
# Send initial player data via Socket.IO
for steamid, player_data in players_for_map.items():
player_update_data = {
"steamid": steamid,
"name": player_data.get("name", "Player"),
"level": player_data.get("level", 0),
"health": player_data.get("health", 0),
"zombies": player_data.get("zombies", 0),
"deaths": player_data.get("deaths", 0),
"players": player_data.get("players", 0),
"score": player_data.get("score", 0),
"ping": player_data.get("ping", 0),
"is_muted": player_data.get("is_muted", False),
"is_authenticated": player_data.get("is_authenticated", False),
"in_limbo": player_data.get("in_limbo", False),
"is_initialized": player_data.get("is_initialized", False),
"permission_level": player_data.get("permission_level", None),
"dataset": player_data.get("dataset", ""),
"position": {
"x": float(player_data.get("pos", {}).get("x", 0)),
"y": float(player_data.get("pos", {}).get("y", 0)),
"z": float(player_data.get("pos", {}).get("z", 0))
}
}
module.webserver.send_data_to_client_hook(
module,
payload=player_update_data,
data_type="player_position_update",
clients=[dispatchers_steamid]
)
# Send initial location data via Socket.IO
for location_id, location_data in locations_for_map.items():
module.webserver.send_data_to_client_hook(
module,
payload={
"location_id": location_id,
"location": location_data
},
data_type="location_update",
clients=[dispatchers_steamid]
)
def options_view(*args, **kwargs):
module = args[0]
dispatchers_steamid = kwargs.get("dispatchers_steamid", None)
# Load templates
template_frontend = module.templates.get_template('manage_locations_widget/view_options.html')
template_view_menu = module.templates.get_template('manage_locations_widget/control_view_menu.html')
# Get current view
current_view = module.get_current_view(dispatchers_steamid)
# Render navigation menu using view registry
options_toggle = module.template_render_hook(
module,
template=template_view_menu,
views=VIEW_REGISTRY,
current_view=current_view,
steamid=dispatchers_steamid,
control_player_location_view='' # No player location in options view
)
data_to_emit = module.template_render_hook(
module,
template=template_frontend,
options_toggle=options_toggle,
widget_options=module.options,
available_actions=module.available_actions_dict,
available_triggers=module.available_triggers_dict,
available_widgets=module.available_widgets_dict
)
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="widget_content",
clients=[dispatchers_steamid],
target_element={
"id": "manage_locations_widget",
"type": "table",
"selector": "body > main > div"
}
)
def special_locations_view(*args, **kwargs):
module = args[0]
dispatchers_steamid = kwargs.get("dispatchers_steamid", None)
# Load templates
template_frontend = module.templates.get_template('manage_locations_widget/view_special_locations.html')
template_view_menu = module.templates.get_template('manage_locations_widget/control_view_menu.html')
# Get current view
current_view = module.get_current_view(dispatchers_steamid)
# Render navigation menu using view registry
options_toggle = module.template_render_hook(
module,
template=template_view_menu,
views=VIEW_REGISTRY,
current_view=current_view,
steamid=dispatchers_steamid,
control_player_location_view=''
)
data_to_emit = module.template_render_hook(
module,
template=template_frontend,
options_toggle=options_toggle
)
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="widget_content",
clients=[dispatchers_steamid],
target_element={
"id": "manage_locations_widget",
"type": "table",
"selector": "body > main > div"
}
)
def edit_view(*args, **kwargs):
module = args[0]
dispatchers_steamid = kwargs.get("dispatchers_steamid", None)
edit_mode = kwargs.get("current_view", None)
location_to_edit_dict = {}
if edit_mode == "edit_location_entry":
location_owner = kwargs.get("location_owner", None)
location_identifier = kwargs.get("location_identifier", None)
location_origin = kwargs.get("location_origin", None)
if all([
location_owner is not None,
location_identifier is not None,
location_origin is not None
]):
location_to_edit_dict = (
module.dom.data
.get(module.get_module_identifier(), {})
.get("elements", {})
.get(location_origin)
.get(location_owner)
.get(location_identifier)
)
if edit_mode == "create_new":
location_to_edit_dict = {
"owner": str(dispatchers_steamid),
"is_enabled": False
}
# Check for prefilled coordinates from map
prefill_coords = (
module.dom.data
.get(module.get_module_identifier(), {})
.get("visibility", {})
.get(dispatchers_steamid, {})
.get("prefill_coordinates", None)
)
if prefill_coords:
location_to_edit_dict["coordinates"] = {
"x": float(prefill_coords.get("x", 0)),
"y": float(prefill_coords.get("y", 0)),
"z": float(prefill_coords.get("z", 0))
}
# Also prefill teleport_entry with same coordinates
location_to_edit_dict["teleport_entry"] = {
"x": float(prefill_coords.get("x", 0)),
"y": float(prefill_coords.get("y", 0)),
"z": float(prefill_coords.get("z", 0))
}
# Load templates
template_frontend = module.templates.get_template('manage_locations_widget/view_create_new.html')
template_view_menu = module.templates.get_template('manage_locations_widget/control_view_menu.html')
control_player_location_view_template = module.templates.get_template(
'manage_locations_widget/control_player_location.html'
)
# Get current view and player coordinates
current_view = module.get_current_view(dispatchers_steamid)
player_coordinates = module.dom.data.get("module_players", {}).get("players", {}).get(dispatchers_steamid, {}).get(
"pos", {"x": 0, "y": 0, "z": 0}
)
# Render navigation menu using view registry
options_toggle = module.template_render_hook(
module,
template=template_view_menu,
views=VIEW_REGISTRY,
current_view=current_view,
steamid=dispatchers_steamid,
control_player_location_view=module.template_render_hook(
module,
template=control_player_location_view_template,
pos_x=player_coordinates["x"],
pos_y=player_coordinates["y"],
pos_z=player_coordinates["z"]
)
)
data_to_emit = module.template_render_hook(
module,
template=template_frontend,
options_toggle=options_toggle,
widget_options=module.options,
location_to_edit_dict=location_to_edit_dict
)
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="widget_content",
clients=[dispatchers_steamid],
target_element={
"id": "manage_locations_widget",
"type": "table",
"selector": "body > main > div"
}
)
def table_row(*args, **kwargs):
module = args[0]
method = kwargs.get("method", None)
updated_values_dict = kwargs.get("updated_values_dict", None)
template_table_rows = module.templates.get_template('manage_locations_widget/table_row.html')
control_edit_link = module.templates.get_template('manage_locations_widget/control_edit_link.html')
control_enabled_link = module.templates.get_template('manage_locations_widget/control_enabled_link.html')
if updated_values_dict is not None:
if method in ["upsert", "update", "edit"]:
active_dataset = module.dom.data.get("module_game_environment", {}).get("active_dataset", None)
for clientid in module.webserver.connected_clients.keys():
current_view = module.get_current_view(clientid)
visibility_conditions = [
current_view == "frontend"
]
if any(visibility_conditions): # only relevant if the table is shown
for player_steamid, locations in updated_values_dict.items():
player_dict = (
module.dom.data
.get("module_players", {})
.get("elements", {})
.get(active_dataset, {})
.get(player_steamid, {})
)
for identifier, location_dict in locations.items():
location_is_selected_by = location_dict.get("selected_by", [])
location_entry_selected = False
if clientid in location_is_selected_by:
location_entry_selected = True
try:
# Sanitize dataset for HTML ID (replace spaces with underscores, lowercase)
sanitized_dataset = module.dom_management.sanitize_for_html_id(updated_values_dict[player_steamid][identifier]["dataset"])
table_row_id = "location_table_row_{}_{}_{}".format(
sanitized_dataset,
str(player_steamid),
str(identifier)
)
# Update location_dict with sanitized dataset for template
location_dict = location_dict.copy()
location_dict["dataset"] = sanitized_dataset
location_dict["dataset_original"] = updated_values_dict[player_steamid][identifier].get("dataset", "")
except KeyError:
table_row_id = "manage_locations_widget"
control_select_link = module.dom_management.get_selection_dom_element(
module,
target_module="module_locations",
dom_element_select_root=[identifier, "selected_by"],
dom_element=location_dict,
dom_element_entry_selected=location_entry_selected,
dom_action_inactive="select_dom_element",
dom_action_active="deselect_dom_element"
)
rendered_table_row = module.template_render_hook(
module,
template=template_table_rows,
location=location_dict,
player_dict=player_dict,
control_select_link=control_select_link,
control_enabled_link=module.template_render_hook(
module,
template=control_enabled_link,
location=location_dict,
),
control_edit_link=module.template_render_hook(
module,
template=control_edit_link,
location=location_dict,
),
css_class=get_table_row_css_class(location_dict)
)
module.webserver.send_data_to_client_hook(
module,
payload=rendered_table_row,
data_type="table_row",
clients=[clientid],
target_element={
"id": table_row_id,
"type": "tr",
"class": get_table_row_css_class(location_dict),
"selector": "body > main > div > div#manage_locations_widget > main > table > tbody"
}
)
else: # table is not visible or current user, skip it!
continue
elif method in ["remove"]: # callback_dict sent us here with a removal notification!
location_origin = updated_values_dict[2]
player_steamid = updated_values_dict[3]
location_identifier = updated_values_dict[-1]
# Sanitize dataset for HTML ID (replace spaces with underscores, lowercase)
sanitized_origin = module.dom_management.sanitize_for_html_id(location_origin)
module.webserver.send_data_to_client_hook(
module,
data_type="remove_table_row",
clients="all",
target_element={
"id": "location_table_row_{}_{}_{}".format(
sanitized_origin,
str(player_steamid),
str(location_identifier)
),
}
)
update_delete_button_status(module, *args, **kwargs)
def update_player_location(*args, **kwargs):
module = args[0]
updated_values_dict = kwargs.get("updated_values_dict", None)
webserver_logged_in_users = module.dom.data.get("module_webserver", {}).get(
"webserver_logged_in_users", []
)
dispatchers_steamid = updated_values_dict.get("steamid")
if dispatchers_steamid not in webserver_logged_in_users:
return
control_player_location_view = module.templates.get_template(
'manage_locations_widget/control_player_location.html'
)
player_coordinates = updated_values_dict.get("pos", {})
data_to_emit = module.template_render_hook(
module,
template=control_player_location_view,
pos_x=player_coordinates["x"],
pos_y=player_coordinates["y"],
pos_z=player_coordinates["z"]
)
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="element_content",
method="replace",
clients=dispatchers_steamid,
target_element={
"id": "current_player_pos"
}
)
def update_selection_status(*args, **kwargs):
module = args[0]
updated_values_dict = kwargs.get("updated_values_dict", None)
location_identifier = updated_values_dict["identifier"]
# Sanitize dataset for HTML ID (replace spaces with underscores, lowercase)
sanitized_dataset = module.dom_management.sanitize_for_html_id(updated_values_dict["dataset"])
module.dom_management.update_selection_status(
*args, **kwargs,
target_module=module,
dom_element_root=[location_identifier],
dom_element_select_root=[location_identifier, "selected_by"],
dom_action_active="deselect_dom_element",
dom_action_inactive="select_dom_element",
dom_element_id={
"id": "location_table_row_{}_{}_{}_control_select_link".format(
sanitized_dataset,
updated_values_dict["owner"],
updated_values_dict["identifier"]
)
}
)
update_delete_button_status(module, *args, **kwargs)
def update_enabled_flag(*args, **kwargs):
module = args[0]
original_values_dict = kwargs.get("original_values_dict", None)
control_enable_link = module.templates.get_template('manage_locations_widget/control_enabled_link.html')
location_origin = original_values_dict.get("dataset", None)
location_owner = original_values_dict.get("owner", None)
location_identifier = original_values_dict.get("identifier", None)
location_dict = (
module.dom.data.get("module_locations", {})
.get("elements", {})
.get(location_origin, {})
.get(location_owner, {})
.get(location_identifier, None)
)
# Sanitize dataset for HTML ID (replace spaces with underscores, lowercase)
location_dict_sanitized = location_dict.copy()
location_dict_sanitized["dataset"] = module.dom_management.sanitize_for_html_id(location_origin)
location_dict_sanitized["dataset_original"] = location_origin
data_to_emit = module.template_render_hook(
module,
template=control_enable_link,
location=location_dict_sanitized,
)
# Sanitize dataset for HTML ID (replace spaces with underscores, lowercase)
sanitized_origin = module.dom_management.sanitize_for_html_id(location_origin)
module.webserver.send_data_to_client_hook(
module,
payload=data_to_emit,
data_type="element_content",
clients="all",
method="update",
target_element={
"id": "location_table_row_{}_{}_{}_control_enabled_link".format(
sanitized_origin,
location_owner,
location_identifier
),
}
)
def update_delete_button_status(*args, **kwargs):
module = args[0]
module.dom_management.update_delete_button_status(
*args, **kwargs,
target_module=module,
dom_element_root=module.dom_element_root,
dom_element_select_root=module.dom_element_select_root,
dom_action="delete_selected_dom_elements",
dom_element_id={
"id": "manage_locations_control_action_delete_link"
}
)
def update_location_on_map(*args, **kwargs):
"""Send location updates to map view via socket.io"""
module = args[0]
method = kwargs.get("method", None)
updated_values_dict = kwargs.get("updated_values_dict", None)
if updated_values_dict is None:
return
# Check which clients are viewing the map
for clientid in module.webserver.connected_clients.keys():
current_view = module.get_current_view(clientid)
if current_view != "map":
continue
if method in ["upsert", "update", "edit"]:
# Send location update for each changed location
active_dataset = module.dom.data.get("module_game_environment", {}).get("active_dataset", None)
# updated_values_dict structure at callback depth 4:
# {location_identifier: {location_data}}
# location_data includes "owner" field
for identifier, location_dict in updated_values_dict.items():
if not isinstance(location_dict, dict):
continue
# Get owner directly from location_dict
owner_steamid = location_dict.get("owner")
if owner_steamid is None:
continue
location_id = f"{active_dataset}_{owner_steamid}_{identifier}"
# Get full location data from DOM if fields are missing in updated_values_dict
# (e.g., when only is_enabled is updated)
full_location_dict = (
module.dom.data
.get("module_locations", {})
.get("elements", {})
.get(active_dataset, {})
.get(owner_steamid, {})
.get(identifier, {})
)
coordinates = location_dict.get("coordinates")
if coordinates is None:
coordinates = full_location_dict.get("coordinates", {})
dimensions = location_dict.get("dimensions")
if dimensions is None:
dimensions = full_location_dict.get("dimensions", {})
location_data = {
"name": location_dict.get("name", full_location_dict.get("name", "Unknown")),
"identifier": identifier,
"owner": owner_steamid,
"shape": location_dict.get("shape", full_location_dict.get("shape", "circle")),
"coordinates": {
"x": float(coordinates.get("x", 0)),
"y": float(coordinates.get("y", 0)),
"z": float(coordinates.get("z", 0))
},
"dimensions": dimensions,
"teleport_entry": location_dict.get("teleport_entry", full_location_dict.get("teleport_entry", {})),
"type": location_dict.get("type", full_location_dict.get("type", [])),
"is_enabled": location_dict.get("is_enabled", full_location_dict.get("is_enabled", False))
}
module.webserver.send_data_to_client_hook(
module,
payload={
"location_id": location_id,
"location": location_data
},
data_type="location_update",
clients=[clientid]
)
elif method in ["remove"]:
# Send location removal
location_origin = updated_values_dict[2]
owner_steamid = updated_values_dict[3]
location_identifier = updated_values_dict[-1]
location_id = f"{location_origin}_{owner_steamid}_{location_identifier}"
module.webserver.send_data_to_client_hook(
module,
payload={
"location_id": location_id
},
data_type="location_remove",
clients=[clientid]
)
widget_meta = {
"description": "shows locations and stuff",
"main_widget": select_view,
"handlers": {
# the %abc% placeholders can contain any text at all, it has no effect on anything but code-readability
# the third line could just as well read
# "module_locations/elements/%x%/%x%/%x%/selected_by": update_selection_status
# and would still function the same as
# "module_locations/elements/%map_identifier%/%steamid%/%element_identifier%/selected_by":
# update_selection_status
"module_locations/visibility/%steamid%/current_view":
select_view,
"module_locations/elements/%map_identifier%/%steamid%":
table_row,
"module_locations/elements/%map_identifier%/%owner_steamid%/%element_identifier%":
update_location_on_map,
"module_locations/elements/%map_identifier%/%steamid%/%element_identifier%/selected_by":
update_selection_status,
"module_locations/elements/%map_identifier%/%steamid%/%element_identifier%/is_enabled":
update_enabled_flag,
"module_players/elements/%map_identifier%/%steamid%/pos":
update_player_location
},
"enabled": True
}
loaded_modules_dict["module_" + module_name].register_widget(widget_name, widget_meta)