On this page
Build your game

Multiplayer

CCDS runs single-player without Photon PUN2. Install and configure the optional PUN2 integration to enable rooms, networked vehicles, synchronised missions, chat and nametags in the same project.

Prerequisites

  1. Import a complete supported Photon PUN2 SDK and allow Unity to finish importing and compiling.
  2. Configure a Photon Realtime App ID in PhotonServerSettings.
  3. Open Tools → BoneCracker Games → CCDS → Multiplayer Setup to check the installation/configuration status. When PUN is installed but unconfigured, Select Photon Settings selects its settings asset.

The integration has been tested with the complete PUN2 2.55 SDK layout. Revalidate SDK removal, reinstall and multiplayer when upgrading Photon.

CCDS's dependency bootstrap manages the BCG_CCDS_PUN scripting symbol automatically from the SDK's source files, assembly definitions and client plugin on disk. An incomplete import or multiple SDK copies do not enable the integration. Do not add scripting symbols manually to bypass detection; repair the SDK import instead.

SDK state Startup and UI
Absent Local gameplay remains available. Multiplayer entry controls and online panels are hidden or disabled, while their authored components and references remain.
Installed, App ID unconfigured Startup stays local even if the saved preference is multiplayer. The editor status directs you to configure Photon settings.
Installed and configured Local gameplay and multiplayer entry points are available. Online startup follows the saved preference; actual multiplayer gameplay still requires joining a room.

Removing the optional SDK does not require deleting CCDS network bindings, scene IDs, UI references or multiplayer save fields. Keep this authoring data so reinstalling the SDK can use it again. Installing the SDK does not by itself connect the player to a room.

Single-player and multiplayer are the same scenes

There is no separate single-player project, scene set, mission set or save format. CCDS_NetworkManager.networkMode records the selected mode:

C#
CCDS_NetworkManager.Instance.SetSinglePlayerMode();
CCDS_NetworkManager.Instance.SetMultiplayerMode();

// Anywhere in gameplay code, prefer this over raw Photon checks
if (CCDS.IsMultiplayer()) { /* ... */ }

CCDS.IsMultiplayer() is the check to use. Hand-rolled PhotonNetwork.IsConnectedAndReady && InRoom tests drift out of agreement with the rest of CCDS.

For capability checks, use the SDK-independent CCDS_NetworkAvailability API:

Member Meaning
IsInstalled The optional PUN integration is compiled into this build
IsConfigured A nonempty, valid Realtime App ID is configured
IsInRoom Photon is connected and ready inside a room
ShouldStartOnline(savedPreference) Installed, configured and the saved preference is true

Reading these properties does not start a connection. SetMultiplayerMode() cannot enable online mode without a configured integration.

The player's preference persists:

C#
CCDS.SetPreferMultiplayer(true);
bool prefers = CCDS.GetPreferMultiplayer();

Unavailable networking changes the effective startup choice, not the stored preference. Vehicle ownership, upgrades and multiplayer progression remain in the same save data while playing locally.

Room configuration

On CCDS_NetworkManager:

Field Type Range Default Purpose
networkMode NetworkMode — SinglePlayer Offline or connected
maxPlayersPerRoom int 2–20 8 Room capacity
playerName string — "Player" Displayed name
roomName string — "" Current or target room
roomIsVisible bool — true Listed in the lobby
roomIsOpen bool — true Accepts new players
missionType string — "FreeRoam" Current room's mission type
defaultMissionType string — "FreeRoam" For new rooms

Read-only state getters: IsInRoomAndReady, InLobby, InRoom, IsMultiplayer, IsMasterClient.

Connection flow

Plain text
ConnectToPhoton()
  → OnConnectedToMaster
  → JoinLobby()
  → CreateRoom() / JoinRoom() / JoinOrCreateRoom() / JoinRandomRoom()
  → OnJoinedRoom
  → PhotonNetwork.Instantiate spawns the player vehicle

The public entry points:

Method Does
ConnectToPhoton() Connect using the configured App ID
JoinLobby() Enter the default lobby
CreateRoom(name) Create a room
CreateRoomWithOptions(name, roomOptions, typedLobby) Create with explicit RoomOptions; only available under BCG_CCDS_PUN
JoinRoom(name) Join a named room
JoinOrCreateRoom(name) Join if it exists, otherwise create
JoinRandomRoom() Matchmake
LeaveRoom() Leave
DisconnectFromPhoton() Disconnect
AutoStartMultiplayer() Connect and matchmake in one call
ConnectToRegion(regionCode) Pin to a Photon region
DisconnectAndLoadScene(sceneIndex) Leave and load a scene cleanly
SignalPlayerReady() Mark this player ready
IsPlayerLoaded(player) Has that Photon Player finished loading; only available under BCG_CCDS_PUN

Wrap calls to SDK-specific overloads, Photon types and PUN callback overrides in #if BCG_CCDS_PUN. The two entries marked above are absent when the integration is not compiled; checking IsInstalled at runtime cannot make an SDK-only signature compile.

Network rates

In CCDS_Settings.asset:

Setting Range Default Meaning
photonSendRate 10–60 30 Updates sent per second
photonSerializationRate 10–60 20 Serializations per second

Keep serialization below send rate. Raising both smooths remote vehicles and costs bandwidth; the shipped 30/20 pair is tuned for vehicles.

Networked vehicles

A player vehicle's authored setup uses CCDS_NetworkBinding, CCDS_Player and RCCP_PhotonSync. The vehicle setup prompt adds the components and records the ordered observers in the binding — see Add Your Own Vehicle.

The binding stores the network view's settings without an SDK component. During Play Mode, when BCG_CCDS_PUN is compiled, CCDS creates the real PhotonView and restores its settings and observer order. Without the SDK, the binding and CCDS component types remain in the prefab. Do not mix an authored PhotonView with an unprepared binding on the same object.

Prefab bindings keep sceneId = 0. Placed scene bindings need persistent IDs and an active CCDS_NetworkSceneBootstrap; use Multiplayer Setup → Register Active Scene and Assign IDs after saving a new scene. See scene registration.

Authority follows Photon: the owner simulates, remotes interpolate. CCDS adds a control lock on top, so it can hold authority across mission transitions:

C#
vehicle.LockControl();
vehicle.UnlockControl();
vehicle.SetControlWithLock(true);
bool locked = vehicle.ControlLockedByCCDS;

While locked, RCCP_PhotonSync will not override canControl or externalControl. Use SetControlWithLock rather than touching RCCP.SetControl directly whenever CCDS owns the decision — otherwise the sync component silently reverts you next frame.

Mission participation

Multiplayer missions derive from ACCDS_MultiplayerMissionBase.

Concept Held in
Who is playing missionPlayers
Where each player spawns playerSpawnAssignments — actor number → spawn index
Spawn slots child transforms under transportToThisLocation
Slot resolution GetPlayerSpawnPosition(), with blocked-slot fallbacks

An empty missionPlayers cancels the mission.

Mission AI activates locally on each participant. Non-participants never enable the mission, so they never see its AI. Do not "fix" this with an RPC — see Create a Mission.

Markers synchronise their logical state and remainingCooldown through OnPhotonSerializeView, with the master client supplying the state. The same marker and mission classes retain their local behavior without the SDK.

Writing networked components

Derive from ACCDS_PunComponent for shared components. With BCG_CCDS_PUN, it extends MonoBehaviourPunCallbacks and exposes the cached PhotonViewComp. Without that symbol, it extends MonoBehaviour and retains virtual local lifecycle hooks. Its scene-manager access is available in both states, and it no longer requires an authored PhotonView.

C#
using BoneCrackerGames.CCDS;

public class MyNetworkedThing : ACCDS_PunComponent {

    public override void OnEnable() {
        base.OnEnable();
        CCDS_Events.OnNetworkPlayerJoined += HandleJoined;
    }

    public override void OnDisable() {
        CCDS_Events.OnNetworkPlayerJoined -= HandleJoined;
        base.OnDisable();
    }

    void HandleJoined(string playerName) {
        CCDS.DebugLog($"{playerName} joined");
    }
}

Keep OnEnable and OnDisable as public override methods that call their base implementations in both SDK states. PUN registers its callback targets there when installed; the local base hooks let the same subscription code compile without PUN. Guard direct PhotonViewComp access and SDK-specific callback methods with #if BCG_CCDS_PUN.

If your component needs a network view, author a CCDS_NetworkBinding on its object. Add serializing components to the binding's ordered observed list; a component that only uses RPCs still needs the view but does not automatically need an observation entry. Keep bindings and scene registration with the content even when developing without PUN.

Traffic sync is the same story: the master client owns traffic and instantiates it over the network. Clients must not spawn their own.

Chat and nametags

Chat is CCDS_ChatManager plus CCDS_UI_ChatPanel. Return sends, Esc closes.

C#
CCDS.ShowChat();
CCDS.HideChat();
CCDS.ToggleChatVisibility();
CCDS.SetChatVisibilityMode(ChatVisibilityMode.ShowOnNewMessages);

ChatVisibilityMode is AlwaysVisible, ShowOnNewMessages or Hidden.

Nametags come from CCDS_UI_NametagManager using CCDS_Settings.nametagPrefab, which must contain a TextMeshProUGUI and a CanvasGroup.

Hold Tab for the scoreboard (CCDS_UI_MultiplayerIngame).

Progression

Persisted per player: multiplayerLevel (starts at CCDS_Settings.defaultMultiplayerLevel, default 1), multiplayerXP, multiplayerWins, multiplayerLosses, totalMultiplayerMatches, favoriteRoomName, preferredRegion, lastOnlineTime.

These save fields and preferMultiplayer remain part of CCDS_SaveData without PUN. Removing or reinstalling the SDK does not automatically erase them.

Testing two clients on one PC

Enable CCDS_Settings.useProcessIdForSaveFile. Each instance then writes its own save file, keyed by process ID, so two clients do not fight over one file.

Turn it off for production builds. Left on, every launch is a new save and players lose their progress.

Events

Sixteen network events are available — connection, room, player and master-client changes. See Events Catalog.

Common problems

Symptom Cause
Multiplayer controls are hidden The optional PUN integration is absent; single-player remains available. Check Multiplayer Setup for status.
Startup stays local despite a multiplayer preference PUN is absent or the Realtime App ID is unconfigured; the saved preference is retained.
PUN files exist but integration stays disabled Complete the SDK import and remove duplicate SDK copies; do not force scripting symbols manually.
Connection never completes Check the Realtime App ID in PhotonServerSettings and the connection status
Photon callbacks never fire OnEnable/OnDisable not public override calling base
Remote vehicles stutter Send/serialization rates too low, or sync component missing
Remote vehicles do not move Check the binding's ordered observers and RCCP_PhotonSync, plus scene IDs for placed content
Traffic desynced between clients Clients spawning traffic locally; only the master may
Non-participants see mission AI Mission AI being spawned over the network
Both test clients share a save useProcessIdForSaveFile disabled
Player spawns inside another Blocked spawn slot — check transportToThisLocation slots
SDK-specific example fails to compile without PUN Guard Photon types and SDK-only methods with #if BCG_CCDS_PUN
Scene loads out of sync Use CCDS scene-loading entry points; direct PhotonNetwork.LoadLevel calls require BCG_CCDS_PUN and an active room

Next

(c) 2014 - 2026 BoneCracker Games - City Car Driving Simulator 1.7Back to top ↑
Document details

Generated from: 02_Build_Your_Game/05_Multiplayer.md
Date: 2026-10-05 15:04
(c) 2014 - 2026 BoneCracker Games - City Car Driving Simulator 1.7