Closed
Conversation
…ing, vehicle support - WebSocket relay server (Cloudflare Worker + Durable Object) - Remote player character cloning with walk/idle/ride animations - Vehicle support for remote players - INI config for relay URL - Extension hook for world transition ROI management
- Replace manual vector math in UpdateTransform with CalcLocalTransform and vec.h macros (LERP3, SET3, MV3, DISTSQRD3, ZEROVEC3) - Remove all SDL_Log debug logging from multiplayer code - Strip extraneous comments that restate the code - Extract CreateAndSpawnPlayer helper to consolidate repeated spawn pattern - Simplify UpdateVehicleState control flow - Remove unused includes (SDL_log.h, mxgeometry/mxmatrix.h)
Add serialization framework using C++ templates and table-driven lookup to sync plant and building state between players. Includes world snapshot routing to requesting peer, relay server Docker support, and fixes for building color sync, ride vehicle visibility, and ARM compilation.
…extension Revert plant/building manager globals back to private class statics (matching master) and use friend declarations for extension access. Move CreateCharacterClone out of LegoCharacterManager into a new CharacterCloner class in the multiplayer extension.
Move world state synchronization logic (snapshots, events, entity mutation routing) into a dedicated WorldStateSync class, reducing NetworkManager from ~790 to ~420 lines.
Split relay.ts into protocol.ts (constants, binary helpers), gameroom.ts (Durable Object), and a thin relay.ts entry point. Replace magic numbers with named constants matching protocol.h. Run wrangler directly as PID 1 in Docker so Ctrl+C shuts down gracefully instead of being swallowed by npx.
…sion Read "multiplayer:room" from INI options (default "default") and use it in Connect() instead of a hardcoded string. Return early if room is blank, matching the relay URL check. Rename p_mgr to p_networkManager for consistency.
Use 'using namespace Extensions' and value_or() instead of verbose fully-qualified names and has_value()/value(), matching the pattern used in legotextureinfo.cpp.
- Tier 1 (plants + buildings): marked as implemented - Tier 2 (NPC customization): downgraded to low value because NPCs are randomly spawned and pathed per-session, so players never see the same NPCs in the same locations - Tier 4 (vehicles): corrected to account for autonomous vehicle coasting after exit (m_worldSpeed is never reset), split into Tier 4a (visibility only) and 4b (coasting position sync), increased effort estimate - Updated priority order, effort summary, and opinion sections
- Configurable room size with relay-enforced ceiling (MAX_PLAYERS_CEILING) - Room config written to isle.ini via OPFS (same pattern as existing settings) - Multiplayer extension disabled in INI for solo play (zero overhead) - Hash-based routing (#r/name) consistent with existing isle.pizza navigation - Three-word Lego Island-themed room names (brave-red-brick style) - Collapsible in-game overlay - Animation research: identified CNs###xx generic animations, click body flip, disassemble/reassemble, and procedural flip; documented playback mechanisms and which are portable to remote players - Animations broadcast-only (not played on local player) - String-based animation names in protocol (matches internal lookup) - Room full: game exits, frontend shows error modal on reload - Removed future layers reference
- Three animation categories: walking (6 options), idle (3 options), one-off emotes (2 options) - Walk/idle selections carried as uint8_t indices in PlayerStateMsg (auto-sync on join) - Emotes remain one-shot MSG_EMOTE messages with emoji popup for local player - WASM exports: mp_set_walk_animation, mp_set_idle_animation, mp_trigger_emote (index-based) - Identified all CNs###xx animations with UI labels and descriptions - Remote player state machine: walk → stationary → idle timeout → emote support - CNs004xx excluded (duplicate of CNs001xx)
…k label - Animation ROI maps: lazily built and cached, invalidated on world change - Room full UX: toast style with sessionStorage to survive page reload - Rename Sneaky walk animation to Leaning
- Remove hardcoded multiplayer config from emscripten config.cpp - Add relay HTTP endpoints for room preview (GET) and creation (POST) with capacity check, CORS headers, and configurable max players - Add WebSocket rejection detection (room full/503) via onclose flag - Add CheckRejected extension call in IsleApp::Tick for clean shutdown through SDL_APP_SUCCESS path instead of calling exit() - Set Module._exitCode in JS for sessionStorage-based toast after reload
* Export MultiplayerExt symbols accessed from isle executable When lego1 is built as a shared library (DLL), symbols used by the isle executable through the Extension<T>::Call() template need to be exported. Add LEGO1_EXPORT to MultiplayerExt::enabled and MultiplayerExt::CheckRejected() which are referenced from isleapp.cpp via the template instantiation. https://claude.ai/code/session_01HHMmowothg25fephi6iidq * Use inline const instead of constexpr for extension function pointers constexpr cannot be used with dllimport function addresses since they are resolved at load time through the IAT, not at compile time. Change to inline const which preserves the single-definition semantics (via inline) while allowing runtime initialization of the function pointers. https://claude.ai/code/session_01HHMmowothg25fephi6iidq * Fix multiplayer extension link errors across DLL boundary When lego1 is a shared library, Extension<T>::Call() instantiated from isle.exe references MultiplayerExt::enabled and CheckRejected() which are not exported. Instead of exporting internal symbols (which also breaks constexpr with dllimport), add an exported IsMultiplayerRejected() wrapper that keeps the Extension<T>::Call() instantiation inside lego1. https://claude.ai/code/session_01HHMmowothg25fephi6iidq * Guard IsMultiplayerRejected with EXTENSIONS ifdef extensions.cpp is only compiled when ISLE_EXTENSIONS is ON, so the wrapper function and its call site need #ifdef EXTENSIONS guards for targets like x86 MSVC where extensions are disabled. https://claude.ai/code/session_01HHMmowothg25fephi6iidq * Move IsMultiplayerRejected definition to multiplayer.cpp The function is declared in multiplayer.h and belongs with the rest of the multiplayer extension code, not in the general extensions.cpp file. https://claude.ai/code/session_01HHMmowothg25fephi6iidq --------- Co-authored-by: Claude <noreply@anthropic.com>
…orts Implement the animation system from the Phase 1 plan: Protocol: Add walkAnimId/idleAnimId fields to PlayerStateMsg (2 extra bytes per 15Hz tick), add MSG_EMOTE (type 9) with EmoteMsg struct, and define shared animation lookup tables (walk: 6 anims, idle: 3, emote: 2). NetworkManager: Store local walk/idle animation indices, include them in every state broadcast, handle incoming MSG_EMOTE by dispatching to the target remote player's TriggerEmote(). Add SetWalkAnimation(), SetIdleAnimation(), SendEmote(), GetPlayerCount() public API. RemotePlayer: Replace per-animation raw pointers with AnimCache struct and lazy m_animCacheMap (name -> ROI map, built on first use, cleared on Despawn). UpdateFromNetwork() detects walk/idle ID changes and swaps the active animation cache. UpdateAnimation() now has three states: moving (configurable walk anim), emote (one-shot with duration tracking, interrupted by movement), and idle (configurable idle anim after 2.5s timeout). Add TriggerEmote() for one-shot emote playback. WASM exports: mp_set_walk_animation(), mp_set_idle_animation(), mp_trigger_emote(), mp_get_player_count() with EMSCRIPTEN_KEEPALIVE. CMakeLists.txt adds EXPORTED_FUNCTIONS and EXPORTED_RUNTIME_METHODS for Svelte ccall/cwrap access. https://claude.ai/code/session_01BEYdu8gXr1QmYwzRRgaEA6
Move WASM exports (mp_set_walk_animation, mp_set_idle_animation, mp_trigger_emote) from multiplayer.cpp into a dedicated wasm_exports.cpp, added to the isle target (guarded by both EMSCRIPTEN and ISLE_EXTENSIONS) so the linker keeps the symbols. Replace the polling mp_get_player_count export with push-based playerCountChanged CustomEvents dispatched from NetworkManager. The count only reflects players visible in the Isle world: null when the local player is outside Isle, filtered by remote player worldId when inside. Remove the now-unused GetPlayerCount() method.
Move all Emscripten-specific multiplayer code under platforms/emscripten/ and introduce an abstract PlatformCallbacks interface for outbound notifications, mirroring the existing NetworkTransport pattern. This removes all #ifdef __EMSCRIPTEN__ blocks from networkmanager.cpp.
* Add feasibility plan for reusing multiplayer animation system for third-person camera Evaluates reusing the multiplayer extension's RemotePlayer animation system (BuildROIMap, AssignROIIndices, ApplyAnimationTransformation) for the local player to enable a third-person camera mode. Conclusion: feasible with only 3 single-line extension hooks added to core game code. https://claude.ai/code/session_01NC3zdQZ4nqEcYjyvStqcdD * WIP: Third-person camera with animation reuse and movement fix * Fix third-person camera bugs: vehicles, remote facing, emote distortion (#2) - Fix spawn pose and building re-entry by applying idle frame 0 and reinitializing on world enable - Handle vehicle transitions: ride animations for small vehicles, first-person fallback for large vehicles and helicopter - Keep vehicle dashboards visible for exit controls - Disable third-person camera for large vehicles, fix ROI cleanup - Move HandleActorExit hook to end of Exit() for immediate reinit - Fix remote player facing 180 degrees wrong by negating direction in BroadcastLocalState when third-person camera is active - Fix Hat Tip emote distortion from compounding transform scale by saving clean parent transform at emote start and restoring after each frame's animation application * DRY cleanup for third-person camera branch - Extract shared DetectVehicleType() to protocol.h/cpp (was duplicated in ThirdPersonCamera and NetworkManager) - Remove no-op HandlePostApplyTransform hook chain (called every frame for every LegoPathActor but did nothing) - Add ThirdPersonCamera::ClearAnimCaches() helper (pattern repeated 5x) - Add AnimUtils::EnsureROIMapVisibility() inline helper (loop repeated 5x across ThirdPersonCamera and RemotePlayer) - Remove redundant static_cast in multiplayer.cpp (UserActor() already returns LegoPathActor*) - Delete THIRD_PERSON_CAMERA_ANIMATION_REUSE_PLAN.md development artifact
- Fix broadcast direction: use IsActive() instead of IsROITurnedAround() so the negate in BroadcastLocalState only fires when movement inversion is active, not based on a default-true flag - Fix vehicle ROI direction for 3rd-person camera: undo Enter()'s TurnAround on small vehicles so the backward-z convention is preserved. Vehicles are placed with ROI z opposite to visual forward, and Enter()'s TurnAround breaks this for 3rd-person rendering. Applied in both OnActorEnter (entering while 3rd-person enabled) and ReinitForCharacter (enabling 3rd-person while already on a vehicle) - Fix vehicle direction on exit: apply extra TurnAround in OnActorExit when 3rd-person is active, since Exit()'s TurnAround assumes Enter()'s TurnAround is still in effect - Add WrappedUpdateWorldData() after manual direction flips in Disable() and ReinitForCharacter() to keep bounding volumes consistent and prevent stale world data from causing momentary camera/direction glitches - Remove unused IsROITurnedAround() method - Fix data race between WASM exports and game thread
* Fix 3rd person camera 180-degree flip after cam anim ends When a cam anim (NPC interaction cutscene) ends, FUN_1004b6d0 places the player actor at the camera's final position using the camera's direction, which uses standard convention (z = visual forward). The 3rd person camera relies on backward-z convention (z = visual backward, from TurnAround). This mismatch caused the camera to face 180 degrees wrong permanently after any cam anim. Add a HandleCamAnimEnd extension hook in FUN_1004b6d0 that, when the 3rd person camera is active, flips the ROI direction back to backward-z and re-establishes the correct camera position.
* Implement display actor override for multiplayer extension Add displayActorIndex to the multiplayer protocol, allowing players to choose any of the 66 character models from g_actorInfoInit via the multiplayer:actor INI setting. The visual display is decoupled from the gameplay actor ID while maintaining backward compatibility. - Protocol: Add displayActorIndex field to PlayerStateMsg and validation helpers - RemotePlayer: Use display actor name for cloning instead of actorId - NetworkManager: Broadcast/handle displayActorIndex, respawn on display change - ThirdPersonCamera: Create/manage display clone ROI for local player override - INI: Read multiplayer:actor setting and resolve to g_actorInfoInit index * Use array syntax for INI option access in display actor setup Consistent with how relayUrl and room are read from options. * Fix display actor ROI handling in 3rd person camera - Fix direction flip targeting display clone instead of native ROI in Disable(), ReinitForCharacter(), and OnCamAnimEnd(). The native ROI is the source of truth for TransformPointOfView and Tick() sync. - Fix use-after-free: DestroyDisplayClone() now nulls m_playerROI when it points to the destroyed clone, preventing dangling pointer access in BuildRideAnimation after a 3rd→1st→3rd person toggle on a vehicle. - Recreate display clone in ReinitForCharacter() vehicle branch. - Extract EnsureDisplayROI() helper to deduplicate clone setup pattern. - Move IsValidDisplayActorIndex() to charactercloner.h, replacing magic number 66 with sizeOfArray(g_actorInfoInit). * Remove display actors plan document
Renders a billboard text bubble showing each remote player's display name. Includes a WASM export to toggle visibility from the frontend. - Bitmap font renderer generates paletted textures for name labels - Billboard quad faces the camera each frame via orientation matrix - Bubble visibility managed globally by NetworkManager toggle - Fix miniwin D3DRMIMAGE constructor code style (static_cast, const)
* WIP: Add character customization to multiplayer Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Refine character customization: fix message buffering, DRY up code, request-based model - Register NetworkManager with TickleManager via HandleCreate hook in LegoOmni::Create(), so packets are processed continuously instead of buffering between Connect() and OnWorldEnabled() - Spawn unspawned remote players in OnWorldEnabled() (created before world was ready) - Switch to request-based customization: HandleROIClick sends MSG_CUSTOMIZE to server, server echoes to all peers, HandleCustomize applies state and plays effects - DRY up SwitchVariant to delegate LOD cloning to ApplyHatVariant - Add ApplyChange helper consolidating the switch-on-changeType pattern - Fix InitFromActorInfo to derive dependent color parts from independent parts (matching Unpack rules) - Remove dead code: m_hasBeenTicked, ApplyCustomizeChange on RemotePlayer, m_localCustomizeState on NetworkManager - Add null ROI checks in HandleCustomize for unspawned players - Move MSG_CUSTOMIZE constant to shared protocol.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix * Fix character customization bugs from code review - BUG-1: Add bounds check in SwitchColor to prevent OOB access from unvalidated network input (p_partIndex could exceed array bounds) - BUG-2: Enforce allowRemoteCustomize on receiver side in HandleCustomize (was only checked on sender side, byppassable by malicious client) - BUG-3: Document stale-state sound asymmetry for remote targets - OBS-1: Remove unused bodyVariantIndex from CustomizeState (never modified, wasted 3 bits per state message) - NAME-1: Fix p_ prefix convention on ApplyCustomizeChange parameters
Add RoomRegistry durable object for tracking public rooms with stale-entry cleanup. GameRoom notifies the registry on connect, disconnect, and creation. Extract duplicated CORS_HEADERS to a shared cors.ts module and remove dead OPTIONS handlers from DOs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Required for Cloudflare free plan to back Durable Objects with SQLite storage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When AttemptReconnect() disconnects a still-CONNECTING socket and creates a new one, the old socket's onclose fires asynchronously after Connect() clears m_disconnectedFlag, setting it back to 1. With connectedFlag=1 and disconnectedFlag=1 both stuck, the state machine bounces between STATE_CONNECTED and STATE_RECONNECTING every frame (30-60x/sec) without ever reaching the backoff timer or attempt limit. Guard all WebSocket event handlers to only modify shared flags when their socket is still the active one in Module._mpSockets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Camera animations compute high listener velocities via CalculateWorldVelocity, and miniaudio's default Doppler factor of 1.0 shifts the pitch/speed of all spatialized sounds accordingly. Disable Doppler on sounds created by the multiplayer AudioPlayer using a friend class to access LegoCacheSound's private ma_sound handle. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Players near multiple location points now see cam animations from all overlapping locations instead of only the nearest one. Location proximity radius reduced from 15 to 5 units. NPC animations unchanged (still proximity-based). JSON output updated from "location" to "locations" array. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PhonemePlayer resolved ROIs by name, but multiplayer participants have unique ROI names (e.g. pepper_mp_8) that don't match the animation actor name in the SI phoneme track (pepper). Pass actor-name-to-ROI aliases from SetupROIs through to PhonemePlayer::Init so it can resolve participant ROIs correctly. Also lower/raise background audio volume for the duration of scene animations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the single ScenePlayer/m_playingAnimIndex with a map of ScenePlayers keyed by animation index, allowing non-overlapping groups of players to play different animations simultaneously. Each player can still only participate in one animation at a time. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The engine derives characterIndex from the last 2 chars of the animation filename, which misses animations with suffixes like "in" (infoman), "p1", "sl". These were incorrectly categorized as e_otherAnim and hidden from multiplayer despite having valid named character performers. Categorize based on whether the animation has at least one named character in its performer mask (g_actorInfoInit indices 0-47 + 54-57) instead of requiring characterIndex >= 0. This adds 23 animations to the catalog. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Manually override 6 spectator-only animations (557, 596, 709-711, 754) as cam anims in the catalog, with 754's location set to Hospital. For animations referencing world-placed models (BIRD, SHARK, CAT) whose LOD data isn't independently registered in the ViewLODListManager, add a scene ROI cloning fallback in SetupROIs. DeepCloneROI recursively clones the ROI hierarchy sharing LOD geometry via refcount, producing independent copies safe for concurrent playback. Moved to AnimUtils as a reusable utility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add vehicle-based filtering to the multiplayer ScenePlayer so that animations requiring a specific vehicle (skateboard, bike, motorcycle) are only offered when the performer is actually riding that vehicle. - Add vehicleMask to CatalogEntry from AnimInfo::m_unk0x2a - Three-state vehicle detection: on foot, on own vehicle, on foreign vehicle - Filter performer animations by vehicle state in eligibility computation - Spectator-only roles remain visible regardless of vehicle state - Host validates vehicle state on interest and re-validates during countdown - Cancel active sessions when local player's vehicle state changes Includes temporary debug logging tagged TODO(vehicle-filter). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rVehicleId GetCharacterVehicleId used hardcoded g_characters[] indices but was called with g_actorInfoInit[] indices. Since g_actorInfoInit has an extra "infoman" entry at index 5, all characters after Laura were off-by-one — bikers got the wrong vehicle and sy (Shiney Doris) fell off the switch entirely, disabling filtering completely. Replace with data-driven lookup (actorInfoInit name → g_characters vehicleId), consolidate duplicate GetVehicleCategory into Catalog, remove dead characterIndex field, fix stale g_characters comments, remove temporary debug logging, and DRY local vehicle state computation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stop active ScenePlayer animation in OnActorEnter/OnActorExit before modifying ride animation state — the ScenePlayer may still hold a reference to the ride vehicle ROI that ClearRideAnimation frees. Deactivate() and OnWorldDisabled() already had this guard. Add alignment padding to MessageHeader (13→14 bytes) so uint16_t fields in packed protocol structs no longer sit at odd offsets (UBSan violation). Breaking wire format change — all clients and relay must update together. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract generic SI file reading and audio extraction from Multiplayer::Animation::Loader into a new Multiplayer::SIReader class. This eliminates the coupling where NetworkManager reached into the Animation namespace to extract horn WAV data, and removes the wasteful intermediate horn cache (LegoCacheSound::Create copies PCM data, so the cache served no purpose after template creation). - New Multiplayer::SIReader owns SI file handle, header-only reading, lazy object loading, and audio track extraction - New Multiplayer::AudioTrack struct (moved from SceneAnimData::AudioTrack) - Animation::Loader delegates SI access via SetSIReader() pointer - NetworkManager owns SIReader, passes it to Loader, uses it directly for horn sound extraction via ExtractFirstAudio() - Consolidate duplicate horn vehicle arrays into single g_hornVehicles[] - Move HornMsg next to EmoteMsg in protocol (both one-shot broadcasts) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ComputeChildOffsets and ApplyHierarchyTransform had the multiplication operands swapped. In row-major convention with translation in row 3: offset = child_world * inverse(parent_world) child_new = offset * new_parent_world Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In large vehicles the third-person camera display ROI is frozen at the entry position, so the name bubble stayed there. Fall back to the actual UserActor ROI when the camera controller is inactive due to a large vehicle, matching how remote players already handle this. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The build exit animations (e.g. c_ips002ro_RunAnim for helicopter) are triggered via FUN_10060dc0 in SpawnPlayer, which bypasses the m_enableCamAnims check. Mark m_playedExitScript = TRUE for all vehicle build states in EnforceDisableNPCs to prevent them from firing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace magic numbers with named constants across third-person camera, multiplayer, and common utilities. Extract duplicated code into shared helpers: CancelExternalAnim, StartEmotePhase, DeriveDependentIndices, ReaddROI, SendFixedMessage. Deduplicate finger-down handling via TryClaimFinger and tighten Extensions::Enable() dispatch with a table-driven approach. Fix missing <functional> include in sceneplayer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The inline `class LegoROI*` in Catalog::GetVehicleState was being resolved within `Multiplayer::Animation` namespace on some compilers (e.g. devkitA64 GCC), creating an incomplete type. Move the forward declaration before the namespace block. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extend the multiplayer animation system to support animations from all three game worlds (ACT1, ACT2, ACT3) while playing in the Isle world. Catalog: Parse DTA files directly for all worlds instead of borrowing from LegoAnimationManager. World-encoded animIndex (top 2 bits = world slot) provides globally unique IDs without wire protocol changes. Loader: Support multiple SI files (isle.si, act2main.si, act3.si) with lazy opening and composite (worldId, objectId) cache keys. WDB: Load missing model LODs from WORLD.WDB for all worlds during catalog refresh, using LegoPartPresenter for parts and LegoModelPresenter for compound models (ray, chptr). Protocol: AnimCompleteMsg now carries animIndex instead of objectId. Also fix pre-existing bugs: - PhonemePlayer UAF when multiple tracks target the same ROI - ModelDbModel buffer overflow on word-aligned strlcpy reads - SIReader UBSan violation on uninitialized filetype enum Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Include 3dmanager/lego3dmanager.h to provide the full class definition, which was only forward-declared via legovideomanager.h. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SI extra data format uses colon as key-value separator (e.g. PTATCAM:HEAD;INFOHAT), matching KeyValueStringParse's delimiter set. The parser incorrectly required an equals sign, so PTATCAM directives were never found and the feature was completely inactive. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
disregard