Files
modolith/mods/discovery_maps/marker-share.lua
T

404 lines
15 KiB
Lua

-- Marker Share System for Persistent Map Mod
-- Allows players to share discovered tiles with each other via chat commands
local S = minetest.get_translator(minetest.get_current_modname())
-- Share a discovered tile with another player
local function share_tile_with_player(sender_name, receiver_name, tile_x, tile_z)
-- Check if sender exists and is online
local sender = minetest.get_player_by_name(sender_name)
if not sender then
return false, "Sender not found"
end
-- Check if receiver exists (can be offline)
local receiver_exists = minetest.player_exists(receiver_name)
if not receiver_exists then
return false, "Player '" .. receiver_name .. "' does not exist"
end
-- Check if sender has discovered this tile
if not persistent_map.get_player_data(sender_name) then
return false, "Sender data not found"
end
local sender_tiles = persistent_map.get_player_data(sender_name).discovered_tiles
local tile_id = string.format("tile_%d_%d", tile_x, tile_z)
if not sender_tiles[tile_id] then
return false, "You haven't discovered tile at coordinates " .. tile_x .. ", " .. tile_z
end
-- Load receiver's data (works for offline players too)
local storage = persistent_map.storage
if not storage then
return false, "Storage not initialized"
end
local receiver_data = storage:get_string("player_" .. receiver_name)
local receiver_tiles = {}
if receiver_data ~= "" then
receiver_tiles = minetest.deserialize(receiver_data) or {}
end
-- Check if receiver already has this tile
if receiver_tiles[tile_id] then
return false, "Player '" .. receiver_name .. "' already has this tile"
end
-- Add tile to receiver's discovered tiles
receiver_tiles[tile_id] = {x = tile_x, z = tile_z}
storage:set_string("player_" .. receiver_name, minetest.serialize(receiver_tiles))
-- If receiver is online, update their runtime data and send the tile
local receiver = minetest.get_player_by_name(receiver_name)
if receiver then
local receiver_player_data = persistent_map.get_player_data(receiver_name)
if receiver_player_data then
receiver_player_data.discovered_tiles[tile_id] = {x = tile_x, z = tile_z}
end
-- Send the tile texture to the receiver
local map_path = persistent_map.map_path
local filename = map_path .. tile_id .. ".png"
minetest.dynamic_add_media({
filepath = filename,
to_player = receiver_name,
}, function(player_name)
minetest.chat_send_player(receiver_name,
S("@1 shared a map tile with you at coordinates @2, @3!", sender_name, tile_x, tile_z))
end)
end
return true, "Tile shared successfully with " .. receiver_name
end
-- Get tile coordinates from player position
local function pos_to_tile_coords(pos)
return math.floor(pos.x / persistent_map.tile_size),
math.floor(pos.z / persistent_map.tile_size)
end
-- Register chat command to share current tile
minetest.register_chatcommand("share_tile", {
params = "<player_name>",
description = S("Share your current discovered tile with another player"),
func = function(name, param)
if param == "" then
return false, S("Usage: /share_tile <player_name>")
end
local player = minetest.get_player_by_name(name)
if not player then
return false, S("Player not found")
end
local pos = player:get_pos()
local tile_x, tile_z = pos_to_tile_coords(pos)
local success, message = share_tile_with_player(name, param, tile_x, tile_z)
return success, message
end,
})
-- Register chat command to share specific tile coordinates
minetest.register_chatcommand("share_tile_coords", {
params = "<player_name> <tile_x> <tile_z>",
description = S("Share a specific discovered tile with another player by coordinates"),
func = function(name, param)
local parts = param:split(" ")
if #parts ~= 3 then
return false, S("Usage: /share_tile_coords <player_name> <tile_x> <tile_z>")
end
local receiver_name = parts[1]
local tile_x = tonumber(parts[2])
local tile_z = tonumber(parts[3])
if not tile_x or not tile_z then
return false, S("Invalid coordinates. Use numbers for tile_x and tile_z")
end
local success, message = share_tile_with_player(name, receiver_name, tile_x, tile_z)
return success, message
end,
})
-- Register chat command to list discovered tiles
minetest.register_chatcommand("list_tiles", {
params = "[player_name]",
description = S("List discovered tiles (your own or another player's if you have permission)"),
privs = {server = true}, -- Only server admins can view other players' tiles
func = function(name, param)
local target_name = param ~= "" and param or name
-- Non-admins can only view their own tiles
if target_name ~= name and not minetest.check_player_privs(name, {server = true}) then
return false, S("You can only view your own tiles")
end
local storage = persistent_map.storage
if not storage then
return false, "Storage not initialized"
end
local player_data = storage:get_string("player_" .. target_name)
if player_data == "" then
return false, S("No tile data found for player @1", target_name)
end
local tiles = minetest.deserialize(player_data) or {}
local tile_list = {}
for tile_id, tile_data in pairs(tiles) do
table.insert(tile_list, string.format("(%d, %d)", tile_data.x, tile_data.z))
end
if #tile_list == 0 then
return true, S("Player @1 has no discovered tiles", target_name)
end
table.sort(tile_list)
return true, S("Player @1 has @2 discovered tiles: @3",
target_name, #tile_list, table.concat(tile_list, ", "))
end,
})
-- Register chat command to share multiple tiles in an area
minetest.register_chatcommand("share_area", {
params = "<player_name> <radius>",
description = S("Share all discovered tiles in a radius around your current position"),
func = function(name, param)
local parts = param:split(" ")
if #parts ~= 2 then
return false, S("Usage: /share_area <player_name> <radius>")
end
local receiver_name = parts[1]
local radius = tonumber(parts[2])
if not radius or radius < 1 or radius > 10 then
return false, S("Radius must be a number between 1 and 10")
end
local player = minetest.get_player_by_name(name)
if not player then
return false, S("Player not found")
end
local pos = player:get_pos()
local center_tile_x, center_tile_z = pos_to_tile_coords(pos)
local shared_count = 0
local failed_count = 0
-- Share tiles in a square area around the player
for dx = -radius, radius do
for dz = -radius, radius do
local tile_x = center_tile_x + dx
local tile_z = center_tile_z + dz
local success, message = share_tile_with_player(name, receiver_name, tile_x, tile_z)
if success then
shared_count = shared_count + 1
else
failed_count = failed_count + 1
end
end
end
return true, S("Shared @1 tiles with @2 (@3 failed)",
shared_count, receiver_name, failed_count)
end,
})
-- Register chat command to share ALL discovered tiles with another player
minetest.register_chatcommand("sharemap", {
params = "<player_name>",
description = S("Share ALL your discovered tiles with another player"),
func = function(name, param)
if param == "" then
return false, S("Usage: /share_all_tiles <player_name>")
end
local receiver_name = param
-- Check if receiver exists (can be offline)
local receiver_exists = minetest.player_exists(receiver_name)
if not receiver_exists then
return false, "Player '" .. receiver_name .. "' does not exist"
end
-- Get sender's discovered tiles
local sender_data = persistent_map.get_player_data(name)
if not sender_data then
return false, "Your player data not found"
end
local sender_tiles = sender_data.discovered_tiles
local tile_count = 0
for _ in pairs(sender_tiles) do
tile_count = tile_count + 1
end
if tile_count == 0 then
return false, "You have no discovered tiles to share"
end
-- Confirm with sender before sharing (large operation)
minetest.chat_send_player(name, S("You are about to share @1 tiles with @2. This may take a moment...", tile_count, receiver_name))
-- Load receiver's data (works for offline players too)
local storage = persistent_map.storage
if not storage then
return false, "Storage not initialized"
end
local receiver_data = storage:get_string("player_" .. receiver_name)
local receiver_tiles = {}
if receiver_data ~= "" then
receiver_tiles = minetest.deserialize(receiver_data) or {}
end
local shared_count = 0
local already_had_count = 0
-- Share all tiles
for tile_id, tile_data in pairs(sender_tiles) do
if not receiver_tiles[tile_id] then
-- Add tile to receiver's discovered tiles
receiver_tiles[tile_id] = {x = tile_data.x, z = tile_data.z}
shared_count = shared_count + 1
else
already_had_count = already_had_count + 1
end
end
-- Save receiver's updated tiles
if shared_count > 0 then
storage:set_string("player_" .. receiver_name, minetest.serialize(receiver_tiles))
end
-- If receiver is online, update their runtime data and send the tiles
local receiver = minetest.get_player_by_name(receiver_name)
if receiver and shared_count > 0 then
local receiver_player_data = persistent_map.get_player_data(receiver_name)
if receiver_player_data then
-- Update runtime data
for tile_id, tile_data in pairs(sender_tiles) do
if not receiver_player_data.discovered_tiles[tile_id] then
receiver_player_data.discovered_tiles[tile_id] = {x = tile_data.x, z = tile_data.z}
end
end
end
-- Send all new tile textures to the receiver
local map_path = persistent_map.map_path
local tiles_to_send = {}
for tile_id, tile_data in pairs(sender_tiles) do
if receiver_tiles[tile_id] and not (already_had_count > 0 and receiver_tiles[tile_id] == tile_data) then
local filename = map_path .. tile_id .. ".png"
table.insert(tiles_to_send, {
filepath = filename,
to_player = receiver_name,
})
end
end
-- Send tiles in batches to avoid overwhelming the system
local function send_tile_batch(batch_start)
local batch_size = 10 -- Send 10 tiles at a time
local batch_end = math.min(batch_start + batch_size - 1, #tiles_to_send)
if batch_start > #tiles_to_send then
-- All tiles sent, notify receiver
minetest.chat_send_player(receiver_name,
S("@1 shared @2 map tiles with you! (@3 were already discovered)",
name, shared_count, already_had_count))
return
end
for i = batch_start, batch_end do
minetest.dynamic_add_media(tiles_to_send[i], function(player_name)
-- Continue with next batch when this batch is complete
if i == batch_end then
minetest.after(0.1, function()
send_tile_batch(batch_end + 1)
end)
end
end)
end
end
if #tiles_to_send > 0 then
send_tile_batch(1)
else
minetest.chat_send_player(receiver_name,
S("@1 shared @2 map tiles with you! (@3 were already discovered)",
name, shared_count, already_had_count))
end
end
local result_message = S("Shared @1 tiles with @2", shared_count, receiver_name)
if already_had_count > 0 then
result_message = result_message .. S(" (@1 were already discovered)", already_had_count)
end
return true, result_message
end,
})
-- Register chat command to accept/reject tile shares (for future expansion)
minetest.register_chatcommand("tile_info", {
params = "[tile_x] [tile_z]",
description = S("Get information about a tile (current tile if no coordinates given)"),
func = function(name, param)
local player = minetest.get_player_by_name(name)
if not player then
return false, S("Player not found")
end
local tile_x, tile_z
if param == "" then
-- Use current position
local pos = player:get_pos()
tile_x, tile_z = pos_to_tile_coords(pos)
else
local parts = param:split(" ")
if #parts ~= 2 then
return false, S("Usage: /tile_info [tile_x] [tile_z]")
end
tile_x = tonumber(parts[1])
tile_z = tonumber(parts[2])
if not tile_x or not tile_z then
return false, S("Invalid coordinates")
end
end
-- Check if player has discovered this tile
local player_data = persistent_map.get_player_data(name)
if not player_data then
return false, S("Player data not found")
end
local tile_id = string.format("tile_%d_%d", tile_x, tile_z)
local discovered = player_data.discovered_tiles[tile_id] ~= nil
-- Convert tile coordinates to world coordinates (center of tile)
local world_x = tile_x * persistent_map.tile_size + persistent_map.tile_size / 2
local world_z = tile_z * persistent_map.tile_size + persistent_map.tile_size / 2
local info = S("Tile (@1, @2):\n", tile_x, tile_z) ..
S("World center: (@1, @2)\n", math.floor(world_x), math.floor(world_z)) ..
S("Discovered: @1", discovered and S("Yes") or S("No"))
return true, info
end,
})
minetest.log("action", "[persistent_map] Marker share system loaded successfully")