Release Notes
3.0.0
Preview
Build 1969 (May 07, 2026)
Breaking Changes
- OnOwnershipRequest signature: bool(ObjectRoot*, PlayerId) -> void(ObjectRoot*, PlayerId). No longer a vote; owner replies asynchronously via Client::RespondToOwnershipRequest(obj, granted)
- C API: obj_give_away_owner → obj_respond_to_ownership_request(client, obj, granted). C#: Object.GiveAwayOwner -> Object.RespondToOwnershipRequest
- Removed Client::SetGiveAwayOwner and Client::RejectOwnershipRequest
- Removed OnObjectOwnerAssigned — use OnObjectOwnerChanged and compare against LocalPlayerId()
Bug Fixes
- Fixed: OnObjectOwnerChanged now also fires on first owner assignment during create
- Fixed: Request cooldown only bumps when the owner is reachable
- Fixed: Other consistency issues with ownership change behaviour
Build 1923 (May 05, 2026)
What's New
- The former
SharedClientSDK is renamed to Fusion Core. Almost every public surface changed: theSharedModenamespace becomesFusionCore, the singleFusionstatic library is split intoPhotonCommon→PhotonMatchmaking→FusionCore, and a new optionalFusionCAPIMODULE exposes a flat C ABI for engine bindings. The single-scene model is replaced by a multi-map session,PlayerPredictedownership andSimulationMode::Authorityenable server-authoritative prediction with input replay, andRealtimeClientgains a fullTask<Result<T>>async API. C++20 is now required
Changes
- Changed: namespace
SharedMode::*→FusionCore::*.SharedModeCompat.his included from every public header for partial source-compat - Changed: single
Fusionstatic library replaced byPhotonCommon→PhotonMatchmaking→FusionCorechain - Added: optional
FusionCAPIMODULE library exposing a flat C ABI — 235+ functions, opaque handles, polled event queue, blittableFusionEvent(28 B), unifiedFusionEventTypeenum (0–18 client / 100–116 realtime async / 200–220 realtime broadcasters), blittableFusionNetworkStats/FusionTrafficStats/FusionTrafficStatsGameLevel/FusionEMAReport. Header:FusionCore/libs/fusion_c_api/include/FusionCAPI.h - Added: cache flag
FUSION_BUILD_CAPI(ON for standalone, OFF for engine presets, always OFF on WASM, STATIC on iOS withFUSION_CAPI_STATICdefine) - Changed: library output filenames now embed the MSVC runtime suffix
_mt/_md(e.g.fusion_windows_x64_release_mt.lib) - Added: cache var
FUSION_MSVC_RUNTIME_LIBRARY(MultiThreaded/MultiThreadedDLL) drives the suffix and prebuilt PhotonRealtime variant filtering - Changed: CMake presets split across
CommonConfigurePresets.json,CommonBuildPresets.json,PlatformPresets.json,StandalonePresets.json,UnrealPresets.json,GodotPresets.json,TestPresets.json. Configure presets named<platform>-<arch>-<engine>[-<runtime>], build presets<platform>-<engine>[-<runtime>]-{debug,release} - Changed: C++ standard bumped to C++20 (required for
Task<T>coroutines) - Changed: CMake minimum bumped to 3.31
- Changed:
Version.his generated fromVersion.h.inat configure time usingversion.txt - Removed:
Client::ChangeScene(index, sequence, data)(single-scene model) - Added:
Client::MapChange(data)returningMap, plusMapAdd(data),MapRemove(map),MapIsValid(map)andGetMaps()for additive multi-map sessions - Changed:
Client::CreateSceneObjectrenamed toClient::CreateMapObject. TakesMapinstead ofuint32_t sceneanduint32_t engineFlagsinstead ofObjectSpecialFlags - Changed:
DestroyModes::SceneChangerenamed toDestroyModes::MapChange - Changed:
RPC_InternalSceneChangerenamed toRPC_InternalMapChange - Added:
RPC_InternalMapAdd,RPC_InternalMapRemove,RPC_InternalOwnershipRequest,RPC_InternalRejectSubObject,RPC_InternalDestroyedMapActors,RPC_InternalForceDestroyObject,RPC_InternalForceAliveObject,RPC_InternalInput,RPC_InternalPlayerInterest,RPC_InternalOwnershipResponse(12 reserved IDs total in 1..1023) - Removed:
OnSceneChange(index, sequence, Data&)broadcaster - Added:
OnMapChange(unordered_map<Map, Data>&, bool initial)broadcaster - Changed: room property
fusion_scene_datarenamed tofusion_map_data(now base64-encodedFusionMapStateBuilderpayload) - Removed: public
ObjectRoot::Scenefield. UseObjectRoot::GetMap()returningId.Map - Changed:
PlayerIdnarrowed fromuint32_ttouint16_t. Sentinels:MasterClientPlayerId = 0xFFFF,PluginPlayerId = 0xFFFE,ObjectOwnerPlayerId = 0xFFFD - Added:
Maptypedef (uint16_t) - Changed:
ObjectIdis now{Origin, Map, Counter}(still 8 bytes —Origin@016-bit,Map@216-bit,Counter@432-bit) - Changed:
ObjectId(packed)decoding is nowOrigin = packed & UINT16_MAX; Map = (packed >> 16) & UINT16_MAX; Counter = packed >> 32 - Changed:
Client::GetNewObjectId()is nowClient::GetNewObjectId(Map map) - Changed:
ObjectTailgrew from 24 bytes to 72 bytes. AddedReserved[8],PredictingPlayer,RejectedSequence,InputSequence,InputTime. RenamedSendRate→RoomSendRate - Changed:
Object::ExtraTailWordsis now 18 (was 6) - Added:
ObjectOwnerModes::PlayerPredicted = 5 - Added:
enum class SimulationMode { Shared = 0, Authority = 1 } - Changed:
RpcFlagsreworked from a struct (uint32_t _value) into anenum classwith bitwise operators and aHasFlag(flags, flag)helper - Added:
RpcFlags::IncorrectTargetForward,DontReplyWithResult,MapIncorrect,MaxDeliveryAttemptsReached, plusErrorFlagsaggregate - Removed:
Rpc::TargetComponent(uint16_t) field - Removed:
Rpc::DescriptorTypeHash(uint64_t) field. Routing now uses(Id, EventHash)only - Added:
Rpc::Sequence(uint64_t) andRpc::DeliveryAttempts(uint32_t) - Removed:
DescriptorTypeHashparameter fromClient::CreateUserRpc. New signature:(id, targetPlayer, targetObject, EventHash, data, dataLength) - Changed:
Client::SendUserRpcnow takesRpc&(non-const) — the SDK assignsSequencein place - Changed:
Client::SetSendRate(obj, rate)renamed toSetRoomSendRate(obj, rate).ResetSendRate(obj)renamed toResetRoomSendRate(obj) - Added:
Client::ResetLocalSendRate(obj)(counterpart to existingSetLocalSendRate) - Removed:
Client::ClearAllKeys,ClearAreaKeys,ClearUserKeys,SetAreaKeys,AddUserKey,RemoveUserKey,GetAllAreaKeys,GetAllUserKeys,GetInterestKeys. Replaced byInterestKeySet - Added:
Client::GetLocalInterestKeys()/GetPlayerInterestKeys(player)returningInterestKeySet&(withSet,Remove,Clear,ClearAreaKeys,ClearUserKeys,SetAreaKeys,AddUserKey,RemoveUserKey,GetAllAreaKeys,GetAllUserKeys,IsDirty,ClearDirtyand area/user encoding in the low bit) - Changed:
Client::Start()now returnsbool(wasvoid) — validatesfusion_configandfusion_map_dataroom properties and returnsfalseon precondition failure - Added:
Client::RefreshRoomCache() - Added:
Client::SetDynamicOwnerCooldown(seconds)/GetDynamicOwnerCooldown()(default1.0/3seconds, configurable at runtime) - Removed:
Object::DynamicOwnerCooldownTimestatic constant - Added:
Client::SetAuthoritySendRate(rate)/GetAuthoritySendRate()(default 30 Hz) - Added: prediction APIs
Client::GetSimulationMode(),GetPredictingPlayer(obj),SetPredictingPlayer(obj, player),IsPredictingPlayer(obj),HasInputAuthority(obj),GetInputSequence(obj),SetInputSequence(obj, seq),NextInputSequence() - Added:
ObjectRoot::QueueInput(dt, payload),ExecuteInputs(dt),GetInputQueueCount(),GetInputQueueDeltaTime(),GetInputTime() - Added:
Client::SetGiveAwayOwner(obj, player)to initiate explicit ownership transfers - Added:
Client::RejectOwnershipRequest(obj)to deny inboundOnOwnershipRequestrequests - Changed:
OnOwnerWasGivenrenamed toOnObjectOwnerAssigned - Changed:
OnObjectPredictionOverriderenamed toOnPredictionOverride - Added: broadcasters
OnRpcError,OnObjectForceAlive,OnSubObjectForceAlive,OnInput,OnPredictionReset,OnOwnershipRequest(returnsbool, aggregate any-truegrants),OnOwnershipResponse - Changed:
OnDestroyedMapActor(uint32_t, ObjectId)simplified toOnDestroyedMapActor(ObjectId)(first parameter dropped) - Removed: public
Object::Headerfield - Added:
Object::EngineBlob(Data),Object::EngineHash(uint64_t),Object::EngineFlags(uint32_t) replacingHeader - Removed:
enum class ObjectSpecialFlags.engineFlags(uint32_t) is supplied toCreate*calls and stored on the base class - Removed:
Object::Statusfield andOBJECT_STATUS_NEW/OBJECT_STATUS_PENDING/OBJECT_STATUS_CREATEDconstants - Added:
ObjectRoot::IsReady()andGetHasReceivedState()lifecycle queries - Removed:
Object::GetBytesSendLastTick,GetBytesReceivedLastTick,ConsumeBytesSendLastTick,ConsumeBytesReceivedLastTick,ResetReceivedBytes - Added: EMA-based stats
Object::GetSendReport()/GetRecvReport(),ObjectRoot::GetCombinedSendReport()/GetCombinedRecvReport(),Client::GetSendReport / GetRecvReport / GetSendStateReport / GetRecvStateReport / GetSendRpcReport / GetRecvRpcReport. ReturnsEMAReport { TotalAvg, TotalAvgPerSecond, CurrentAvgPerSecond, Min, Max }. New header:EMA.h - Removed: public
ObjectChild::TargetObjectHash(uint32_t) field - Changed:
Client::CreateSubObjectparametertargetObjectHash(uint32_t) renamed toengineHash(uint64_t, widened);ObjectSpecialFlagsparameter replaced byuint32_t engineFlags - Changed:
ObjectChild::Parentis now private. Access via staticObjectChild::GetParent(obj) - Changed: most
ObjectRootstate is private with getters (GetMap,GetOwner,GetOwnerMode,GetTime,IsReady,GetHasReceivedState,GetPluginVersion,GetClientVersion,GetSubObjects) - Changed:
ObjectRoot::RequiredObjects()returnsstd::span<ObjectId>(wasObjectId*) - Changed:
Object::StringHeapis lazy-allocated ({0}); was pre-allocated to 1024 bytes - Changed:
Object::SetHasValidData()no longer takes aboolargument (always sets the flag) - Changed:
LogLevelenum is now a bitmask (Trace = 1 << 0...Error = 1 << 4) - Added:
WriteBuffer::WriteMap(Map)/ReadBuffer::ReadMap()wire codec for the newMaptypedef - Added: public
PhotonCommontypes:Broadcaster<Sig>,Subscription,ScopedSubscription,SubscriptionBag,Result<T, CodeT>,Task<T>(C++20 coroutine),Error<CodeT>,LogOutput,LogUtils,SpanCompat,StringType,StringViewType,CoroutineCompat,PhotonAssert - Changed: every long-running
RealtimeClientoperation now returnsTask<Result<T>>(Connect / Reconnect / Disconnect / CreateRoom / JoinRoom / JoinOrCreateRoom / JoinRandomRoom / JoinRandomOrCreateRoom / LeaveRoom / JoinLobby / LeaveLobby / GetLobbyStats / FindFriends / WebRpc / AvailableRegions / SelectRegion) - Added:
RealtimeClientbroadcastersOnRoomJoined,OnRoomLeft,OnDirectConnectionEstablished,OnDirectConnectionFailed,OnCustomOperationResponse,OnCacheSliceChanged,OnPropertiesChangeFailed,OnAppStatsUpdated,OnWarning - Added:
RealtimeClient::SendCustomOperation,SendCustomAuthData,SetAutoJoinLobby,GetCountPlayersIngame,GetCountGamesRunning,GetCountPlayersOnline - Added: public
PhotonMatchmakingoption/value typesClientConstructOptions,ConnectOptions,CreateRoomOptions,JoinRoomOptions,MatchmakingOptions,EventOptions,DirectMessageOptions,MutableRoomView,PlayerView,RoomListing,LobbyStats,FriendInfo,NetworkStats,TrafficStats,TrafficStatsGameLevel,WebFlags,WebRpcResponse,PropertyValue,AuthenticationValues,RegionInfo,ConnectionState,DisconnectCause,LobbyType,MatchmakingMode,RegionSelectionMode,ServerType,DirectMode,ReceiverGroup,CustomAuthenticationType,EventCache,EventCallback,ConnectionProtocol,SerializationProtocol,ErrorCode - Added:
FusionRoomProperties.hexposing canonical room property keys (Config/MapData/SdkVersion),FusionConfigDefaults::Json,FusionMapStateBuilder::Build/Empty/BuildBase64/EmptyBase64,FusionSdkVersionBuilder::ToBytes/ToBase64,Base64::Encode/Decode - Added: CAPI helpers
fusion_room_property_key_config / _map_data / _sdk_version,fusion_config_defaults_json,fusion_map_state_empty_base64,fusion_sdk_version_current_base64,fusion_base64_encode / _decode - Added: CAPI web-flag bitmask
FUSION_WEB_FLAG_HTTP_FORWARD,FUSION_WEB_FLAG_SEND_AUTH_COOKIE,FUSION_WEB_FLAG_SEND_SYNC,FUSION_WEB_FLAG_SEND_STATE - Added:
Tools/FusionCore/Fusion{Config,Build,Install,ConfigBuildInstallAll,PackagePlugin}.ps1PowerShell wrappers around the preset matrix - Added:
Tools/Common/{Logging,TeamCity}/*.psm1shared PowerShell modules - Added:
Tools/Changelog/generate_changelog.pyPython changelog generator (withmapping.jsoncategorization) - Changed: GoogleTest 1.17.0 fetched via
FetchContentwhenPHOTON_BUILD_TESTS=ON.gtest_force_shared_crtis matched toFUSION_MSVC_RUNTIME_LIBRARY - Migration Notes
-
- Global rename
SharedMode::→FusionCore::(the includedSharedModeCompat.hcovers some callers; plan for a real rename)
- Global rename
-
PlayerIdisuint16_tandObjectId's middle 16 bits are nowMap— re-derive any custom packing or wire serialization. Sentinels moved from the0xFFFFFFFFfamily to the0xFFFFfamily
-
ObjectTailis 72 bytes (ExtraTailWords = 18); recompute any direct word offsets and renameSendRate→RoomSendRate
-
- Replace
ChangeScene(idx, seq, data)withMapChange(data)(orMapAdd); subscribe toOnMapChange(maps, initial)and diff the table; swapDestroyModes::SceneChange→MapChange
- Replace
-
- Replace
CreateSceneObjectwithCreateMapObject. PassMapinstead ofuint32_t sceneanduint32_t engineFlagsinstead ofObjectSpecialFlags. For sub-objects,targetObjectHashbecomesengineHash(uint64_t)
- Replace
-
- Drop
DescriptorTypeHashfromCreateUserRpc;SendUserRpctakesRpc&(non-const). ReplaceRpcFlags{._value=...}with the strong enum andHasFlag()
- Drop
-
- Rename
SetSendRate→SetRoomSendRateandResetSendRate→ResetRoomSendRate. PairSetLocalSendRatewith the newResetLocalSendRate
- Rename
-
- Client-level interest-key methods are gone; use
Client::GetLocalInterestKeys()/GetPlayerInterestKeys(player)and call methods on the returnedInterestKeySet
- Client-level interest-key methods are gone; use
-
- Drop reads of
Object::Status/SpecialFlags/Headerand the per-frame byte counters. UseObjectRoot::IsReady()/GetHasReceivedState(),EngineBlob/EngineFlags/EngineHash, and the EMA reports
- Drop reads of
-
- Subscribe to
OnObjectOwnerAssigned(replacesOnOwnerWasGiven) andOnPredictionOverride(replacesOnObjectPredictionOverride); addOnPredictionResetif you usePlayerPredicted
- Subscribe to
-
- Initiate ownership transfers via
Client::SetGiveAwayOwner(obj, player); grant inbound requests by returningtruefromOnOwnershipRequest; deny viaClient::RejectOwnershipRequest(obj)
- Initiate ownership transfers via
-
- For C ABI integrations, switch to
FusionCAPI.h(the old SDK had no C ABI). For CMake on MSVC, setFUSION_MSVC_RUNTIME_LIBRARYtoMultiThreadedorMultiThreadedDLLand pick the matching preset
- For C ABI integrations, switch to