Prevent players from hiding Fog of War layers
Some checks are pending
Windows Release / windows-x86_64 (push) Waiting to run
Some checks are pending
Windows Release / windows-x86_64 (push) Waiting to run
Clients could toggle fog visibility (and fade alpha); DisplayData synced that to everyone and defeated FoW. Block client set_visible/set_alpha, clamp host-side DisplayData updates, and ignore eye toggles for fog. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
fea392b65d
commit
3140f9bb55
4 changed files with 31 additions and 3 deletions
2
FORK.md
2
FORK.md
|
|
@ -33,4 +33,4 @@ Not automatic on import. Choose **Paste Image + Freistellen** from the canvas ri
|
||||||
|
|
||||||
## Fog of War / layer lock (host)
|
## Fog of War / layer lock (host)
|
||||||
|
|
||||||
In the layer panel (host / local file only): mark a leaf layer as **Fog of War layer** (must sit **above** the content it covers), then paint fog with brush/shapes on that layer. Players receive a **stripped** world — only objects **fully** covered by opaque fog (not merely overlapping) are omitted from join sync and from their save. Partially covered images stay. Moving or reshaping fog (or content under it) live-updates clients: newly covered objects are unloaded, revealed ones are sent again with their resources. The host file stays complete. Fog draws at the layer’s real alpha (1.0 = fully opaque for host and players). **Lock for players** blocks client edits on a normal layer; fog layers are always locked for players (move/edit/erase rejected on the host).
|
In the layer panel (host / local file only): mark a leaf layer as **Fog of War layer** (must sit **above** the content it covers), then paint fog with brush/shapes on that layer. Players receive a **stripped** world — only objects **fully** covered by opaque fog (not merely overlapping) are omitted from join sync and from their save. Partially covered images stay. Moving or reshaping fog (or content under it) live-updates clients: newly covered objects are unloaded, revealed ones are sent again with their resources. The host file stays complete. Fog draws at the layer’s real alpha (1.0 = fully opaque for host and players). **Lock for players** blocks client edits on a normal layer; fog layers are always locked for players (move/edit/erase rejected on the host). Players also **cannot hide or fade** a fog layer (eye / alpha); only the host can toggle fog visibility, and the host rejects client DisplayData that would strip FoW.
|
||||||
|
|
|
||||||
0
linuxinstall/install-local.sh
Normal file → Executable file
0
linuxinstall/install-local.sh
Normal file → Executable file
|
|
@ -20,6 +20,7 @@
|
||||||
#include "DrawingProgramLayerFolder.hpp"
|
#include "DrawingProgramLayerFolder.hpp"
|
||||||
#include <cereal/types/string.hpp>
|
#include <cereal/types/string.hpp>
|
||||||
#include "DrawingProgramLayer.hpp"
|
#include "DrawingProgramLayer.hpp"
|
||||||
|
#include "FogStripper.hpp"
|
||||||
#include "../../World.hpp"
|
#include "../../World.hpp"
|
||||||
#include "../../MainProgram.hpp"
|
#include "../../MainProgram.hpp"
|
||||||
#include "SerializedBlendMode.hpp"
|
#include "SerializedBlendMode.hpp"
|
||||||
|
|
@ -258,6 +259,9 @@ const std::string& DrawingProgramLayerListItem::get_name() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawingProgramLayerListItem::set_alpha(DrawingProgramLayerManager& layerMan, float newAlpha) const {
|
void DrawingProgramLayerListItem::set_alpha(DrawingProgramLayerManager& layerMan, float newAlpha) const {
|
||||||
|
// Players must not fade fog out — same authority as visibility.
|
||||||
|
if(is_fog_layer() && !FogStripper::world_is_host(layerMan.drawP.world))
|
||||||
|
return;
|
||||||
if(displayData && displayData->alpha != newAlpha) {
|
if(displayData && displayData->alpha != newAlpha) {
|
||||||
displayData->alpha = newAlpha;
|
displayData->alpha = newAlpha;
|
||||||
layerMan.drawP.drawCache.clear_own_cached_surfaces();
|
layerMan.drawP.drawCache.clear_own_cached_surfaces();
|
||||||
|
|
@ -270,6 +274,9 @@ float DrawingProgramLayerListItem::get_alpha() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawingProgramLayerListItem::set_visible(DrawingProgramLayerManager& layerMan, bool newVisible) const {
|
void DrawingProgramLayerListItem::set_visible(DrawingProgramLayerManager& layerMan, bool newVisible) const {
|
||||||
|
// Fog of War is host-controlled; clients hiding it would sync and strip FoW for everyone.
|
||||||
|
if(is_fog_layer() && !FogStripper::world_is_host(layerMan.drawP.world))
|
||||||
|
return;
|
||||||
if(displayData && displayData->visible != newVisible) {
|
if(displayData && displayData->visible != newVisible) {
|
||||||
displayData->visible = newVisible;
|
displayData->visible = newVisible;
|
||||||
layerMan.drawP.drawCache.clear_own_cached_surfaces();
|
layerMan.drawP.drawCache.clear_own_cached_surfaces();
|
||||||
|
|
@ -393,6 +400,24 @@ void DrawingProgramLayerListItem::register_class(World& w) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
w.delayedUpdateObjectManager.register_class<DisplayData>(w.netObjMan, NetworkingObjects::DelayUpdateSerializedClassManager::CustomConstructors<DisplayData>{
|
w.delayedUpdateObjectManager.register_class<DisplayData>(w.netObjMan, NetworkingObjects::DelayUpdateSerializedClassManager::CustomConstructors<DisplayData>{
|
||||||
|
.readUpdate = [](DisplayData& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& c) {
|
||||||
|
DisplayData incoming;
|
||||||
|
a(incoming);
|
||||||
|
// Client→host: never allow stripping fog, unlocking, hiding, or fading fog.
|
||||||
|
if(c) {
|
||||||
|
if(o.fogLayer || incoming.fogLayer) {
|
||||||
|
incoming.fogLayer = true;
|
||||||
|
incoming.playerLocked = true;
|
||||||
|
incoming.visible = true;
|
||||||
|
incoming.alpha = o.alpha;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
incoming.fogLayer = false;
|
||||||
|
incoming.playerLocked = o.playerLocked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
o = incoming;
|
||||||
|
},
|
||||||
.postUpdateFunc = [&](DisplayData& o) {
|
.postUpdateFunc = [&](DisplayData& o) {
|
||||||
(void)o;
|
(void)o;
|
||||||
w.drawProg.drawCache.clear_own_cached_surfaces();
|
w.drawProg.drawCache.clear_own_cached_surfaces();
|
||||||
|
|
|
||||||
|
|
@ -144,11 +144,14 @@ void DrawingProgramLayerManagerGUI::setup_list_gui() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
gui.set_z_index_keep_clipping_region(gui.get_z_index() + 1, [&] {
|
gui.set_z_index_keep_clipping_region(gui.get_z_index() + 1, [&] {
|
||||||
|
// Fog stays visible for players — eye is informational only on clients.
|
||||||
|
const bool fogVisibilityLocked = layer->is_fog_layer() && !FogStripper::world_is_host(world);
|
||||||
svg_icon_button(gui, "visible button", layer->get_visible() ? "data/icons/eyeopen.svg" : "data/icons/eyeclose.svg", {
|
svg_icon_button(gui, "visible button", layer->get_visible() ? "data/icons/eyeopen.svg" : "data/icons/eyeclose.svg", {
|
||||||
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
|
.drawType = SelectableButton::DrawType::TRANSPARENT_ALL,
|
||||||
.size = TreeListing::ENTRY_HEIGHT,
|
.size = TreeListing::ENTRY_HEIGHT,
|
||||||
.onClick = [&, layer] {
|
.onClick = [&, layer, fogVisibilityLocked] {
|
||||||
layer->set_visible(layerMan, !layer->get_visible());
|
if(!fogVisibilityLocked)
|
||||||
|
layer->set_visible(layerMan, !layer->get_visible());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue