[Add] FishNet

This commit is contained in:
2026-03-30 20:11:57 +07:00
parent ee793a3361
commit c22c08753a
1797 changed files with 197950 additions and 1 deletions
@@ -0,0 +1,94 @@
using FishNet.Managing.Scened;
using FishNet.Object;
using UnityEngine;
namespace FishNet.Demo.AdditiveScenes
{
public class LevelLoader : NetworkBehaviour
{
private void OnTriggerEnter(Collider other)
{
if (!IsServerStarted)
return;
Player player = GetPlayerOwnedObject(other);
if (player == null)
return;
/* Create a lookup handle using this objects scene.
* This is one of many ways FishNet knows what scene to load
* for the clients. */
SceneLookupData lookupData = new(gameObject.scene);
SceneLoadData sld = new(lookupData)
{
/* Set automatically unload to false
* so the server does not unload this scene when
* there are no more connections in it. */
Options = new()
{
AutomaticallyUnload = false
},
/* Also move the client object to the new scene.
* This step is not required but may be desirable. */
MovedNetworkObjects = new NetworkObject[] { player.NetworkObject },
// Load scenes as additive.
ReplaceScenes = ReplaceOption.None,
// Set the preferred active scene so the client changes active scenes.
PreferredActiveScene = new(lookupData)
};
SceneManager.LoadConnectionScenes(player.Owner, sld);
}
private void OnTriggerExit(Collider other)
{
if (!IsServerStarted)
return;
Player player = GetPlayerOwnedObject(other);
if (player == null)
return;
/* Create a lookup handle using this objects scene.
* This is one of many ways FishNet knows what scene to load
* for the clients. */
SceneLookupData lookupData = new(gameObject.scene);
/* Tell server to keep unused when unloading. This will keep
* the scene even if there are no connections.
* This varies from AutomaticallyUnload slightly;
* automatically unload will remove the scene on the server
* if there are no more connections, such as if players
* were to disconnect. But when manually telling a scene to
* unload you must tell the server to keep it even if unused,
* if that is your preference. */
SceneUnloadData sud = new(lookupData)
{
Options = new()
{
Mode = UnloadOptions.ServerUnloadMode.KeepUnused
}
};
SceneManager.UnloadConnectionScenes(player.Owner, sud);
}
/// <summary>
/// Returns a Player script if the object is a player.
/// </summary>
/// <param name = "other"></param>
/// <returns></returns>
private Player GetPlayerOwnedObject(Collider other)
{
/* When an object exits this trigger unload the level for the client. */
Player player = other.GetComponent<Player>();
// Not the player object.
if (player == null)
return null;
// No owner??
if (!player.Owner.IsActive)
return null;
return player;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 02e5160f1a9b4d047a6cbdb3f094a86d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs
uploadId: 866910
@@ -0,0 +1,93 @@
using FishNet.Connection;
using FishNet.Object;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace FishNet.Demo.AdditiveScenes
{
public class Player : NetworkBehaviour
{
[SerializeField]
private Transform _ownerObjects;
[SerializeField]
private float _moveRate = 2f;
private List<Waypoint> _wayPoints = new();
private int _goalIndex;
private Vector3 _goalOffset;
private bool _foundWaypoints;
public override void OnOwnershipClient(NetworkConnection prevOwner)
{
_ownerObjects.gameObject.SetActive(IsOwner);
}
private void Update()
{
if (!TryFindWaypoints())
return;
Vector3 posGoal = _wayPoints[_goalIndex].transform.position + _goalOffset;
transform.position = Vector3.MoveTowards(transform.position, posGoal, _moveRate * Time.deltaTime);
Vector3 lookDirection = (posGoal - transform.position).normalized;
// Rotate to goal if there is a look direction.
if (lookDirection != Vector3.zero)
{
Quaternion rot = Quaternion.LookRotation((posGoal - transform.position).normalized, transform.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, rot, 270f * Time.deltaTime);
}
// If at goal set next goal.
if (transform.position == posGoal)
{
_goalIndex++;
// Reset index to 0 if at last goal.
if (_goalIndex >= _wayPoints.Count)
_goalIndex = 0;
}
}
private bool TryFindWaypoints()
{
if (_foundWaypoints)
return true;
//Only the server uses waypoints.
if (!IsServerStarted)
return false;
_wayPoints = FindObjectsOfType<Waypoint>().ToList();
/* There are expected 4 waypoints in this test -- if only
* one is found then the other scenes did not load yet. */
if (_wayPoints.Count != 4)
return false;
/* Stagger spawn position slightly depending on player count.
* Also inverse direction so players cross each other when more
* than one. This is just demo fanciness. */
if (ServerManager.Clients.Count % 2 == 0)
{
_goalOffset = new(-0.5f, 0f, 0f);
_wayPoints = _wayPoints.OrderBy(x => x.WaypointIndex).ToList();
}
else
{
_goalOffset = new(0.5f, 0f, 0f);
_wayPoints = _wayPoints.OrderByDescending(x => x.WaypointIndex).ToList();
}
// Snap to current waypoint.
transform.position = _wayPoints[0].transform.position + _goalOffset;
// Set goal to next waypoint.
_goalIndex = 1;
_foundWaypoints = true;
return true;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c8541d1cca4da7043b84a0d563939dea
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs
uploadId: 866910
@@ -0,0 +1,42 @@
using FishNet.Managing.Scened;
using FishNet.Object;
using GameKit.Dependencies.Utilities.Types;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace FishNet.Demo.AdditiveScenes
{
public class ServerScenePrewarmer : NetworkBehaviour
{
[SerializeField]
[Scene]
private string[] _scenes = new string[0];
public override void OnStartServer()
{
/* Load all the needed scenes at once.
* This is not really required in most situations
* but the server uses waypoints from each scene
* to move the players. The waypoints won't exist unless
* all scenes do. This can be seen in the Player
* script. */
/* Load scenes using the FishNet SceneManager, with automaticallyUnload
* as false. This will keep the scenes from unloading when a client
* disconnects or leaves the scene. */
foreach (string item in _scenes)
{
SceneLookupData lookupData = new(item);
SceneLoadData sld = new(lookupData)
{
Options = new()
{
AutomaticallyUnload = false
}
};
SceneManager.LoadConnectionScenes(sld);
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b06d5bb8c7e0da04581df0b9454b81b7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs
uploadId: 866910
@@ -0,0 +1,9 @@
using UnityEngine;
namespace FishNet.Demo.AdditiveScenes
{
public class Waypoint : MonoBehaviour
{
public byte WaypointIndex;
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 70028e1b1708627408637ab9a3cd6bd4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 207815
packageName: 'FishNet: Networking Evolved'
packageVersion: 4.6.22R
assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs
uploadId: 866910