Compare commits

..

20 Commits

Author SHA1 Message Date
APEX FIGHT
cf607fa060 Merge branch 'main' of https://gitea.apexfight.net/apex/CSNC 2025-02-24 21:52:36 -05:00
APEX FIGHT
ef87716a8c added timeout to CSNC server 2025-02-24 21:52:30 -05:00
8459e7df21 Update README.md 2025-01-03 05:46:18 +00:00
9785da2e0d Update Client/Unity/README.md 2025-01-03 05:42:03 +00:00
e67060aaf7 Update README.md 2025-01-03 05:41:41 +00:00
a84cb21d8b Update README.md 2025-01-03 05:40:32 +00:00
aa1be3c082 Update README.md 2025-01-03 05:34:50 +00:00
APEX FIGHT
62a1d22528 added tick interval options (to reduce network load from 80tps lololol) 2025-01-03 00:33:48 -05:00
APEX FIGHT
ef941cd344 added uuid support for synchronization 2025-01-02 18:22:56 -05:00
APEX FIGHT
0810be6809 clearer documentation 2025-01-02 18:22:33 -05:00
APEX FIGHT
7d799457f6 clearer naming 2025-01-02 18:22:20 -05:00
APEX FIGHT
2a445343a2 meta 2025-01-02 18:22:03 -05:00
APEX FIGHT
af9e800716 gameobjectmanager 2025-01-02 18:21:58 -05:00
APEX FIGHT
72113dc2f2 prefab update 2025-01-02 18:21:48 -05:00
APEX FIGHT
a83220ea7c method call to sync objects 2025-01-02 18:21:37 -05:00
APEX FIGHT
3faaba9d0c added object uuid for synchronization 2025-01-02 18:20:54 -05:00
APEX FIGHT
3a30091559 made private public 2025-01-02 18:20:15 -05:00
APEX FIGHT
51132ceb5c updated csncobject class 2025-01-02 18:20:02 -05:00
APEX FIGHT
258f299fa0 default eztransform constructor 2025-01-02 18:19:24 -05:00
APEX FIGHT
a06db78d42 gameobjectmanager 2025-01-02 18:19:09 -05:00
9 changed files with 207 additions and 28 deletions

View File

@@ -6,9 +6,13 @@ using UnityEngine.Networking;
public class CSNC : MonoBehaviour
{
public float tickInterval = 5f; // x times per sec
public string ip;
private class EZtransform //ez consistent serialization of transforms
public GameObjectManager manager;
private float lastPing = 0; //last time connected to server
public class EZtransform //ez consistent serialization of transforms
{
public List<float> position;
public List<float> rotation;
@@ -26,26 +30,38 @@ public class CSNC : MonoBehaviour
rotation.Add(transform.rotation.z);
rotation.Add(transform.rotation.w);
}
}
private class CSNCObject
public EZtransform()
{
position = new List<float>();
rotation = new List<float>();
}
}
public class CSNCObject
{
public string id;
public string type;
public EZtransform transform;
public CSNCObject(string type, Transform trans)
public CSNCObject()
{
id = "";
type = "";
transform = new EZtransform();
}
public CSNCObject(string type, Transform trans, string id)
{
this.id = id;
this.type = type;
this.transform = new EZtransform(trans);
}
public CSNCObject(string type, EZtransform trans)
public CSNCObject(string type, EZtransform trans, string id)
{
this.id = id;
this.type = type;
this.transform = trans;
}
}
private class SendData
public class SendData
{
public string uuid;
public List<CSNCObject> gameObjects;
@@ -64,9 +80,13 @@ public class CSNC : MonoBehaviour
void FixedUpdate()
{
if (Time.timeSinceLevelLoad - (1000f / tickInterval) > lastPing)
{
lastPing = Time.timeSinceLevelLoad;
IEnumerator req = request();
StartCoroutine(req);
}
}
IEnumerator request()
{
//Debug.Log(GameObjectRegistry.instance.registeredObjects);
@@ -74,7 +94,7 @@ public class CSNC : MonoBehaviour
foreach (SyncData obj in GameObjectRegistry.instance.registeredObjects)
{
gameObjects.Add(new CSNCObject(obj.type, obj.transform));
gameObjects.Add(new CSNCObject(obj.type, obj.transform, obj.id));
}
SendData sendData = new SendData(gameObjects);
@@ -90,9 +110,7 @@ public class CSNC : MonoBehaviour
}
else
{
Debug.Log(req.downloadHandler.text);
manager.SyncObjects(JsonConvert.DeserializeObject<GameObjectManager.RecievedData>(req.downloadHandler.text));
}
}
}

View File

@@ -11,6 +11,7 @@ GameObject:
- component: {fileID: 7121321073155658785}
- component: {fileID: 7629741995735410931}
- component: {fileID: 5596437124550187285}
- component: {fileID: 1768507704600374837}
m_Layer: 0
m_Name: CSNC
m_TagString: Untagged
@@ -45,6 +46,9 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 1c20e30fdef1b0841aa0fdb48748a6a6, type: 3}
m_Name:
m_EditorClassIdentifier:
uuid:
gameObjects: []
ids: []
--- !u!114 &5596437124550187285
MonoBehaviour:
m_ObjectHideFlags: 0
@@ -57,3 +61,17 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 6772f36d5b93fd5498e8f3f1d8155ff2, type: 3}
m_Name:
m_EditorClassIdentifier:
ip:
manager: {fileID: 1768507704600374837}
--- !u!114 &1768507704600374837
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 9009233966360587498}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7f3dbff6bcfe57f4fb9ba717c6fecc0e, type: 3}
m_Name:
m_EditorClassIdentifier:

View File

@@ -0,0 +1,99 @@
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameObjectManager : MonoBehaviour
{
/*
This MonoBehaviour Synchronizes the position of All the game objects recieved from other clients
*/
// ID GAMEOBJECT+TYPE
Dictionary<string, GameObjectDataWrapper> inSceneGameObjects = new Dictionary<string, GameObjectDataWrapper>();
void Start()
{
}
public class RecievedData
{
/*
{
"gameObjects": //list of CSNCObjects
[
{
"id": "uuid here",
"type": "razorblade",
"transform": {
"position": [
0.0,
0.53,
0.0
],
"rotation": [
0.0,
0.0,
0.0,
1.0
]
}
},
]
}
*/
public RecievedData(List<CSNC.CSNCObject> gameObjects)
{
this.gameObjects = gameObjects;
}
public List<CSNC.CSNCObject> gameObjects;
}
public class GameObjectDataWrapper
{
public GameObject gameObject;
public string type;
public GameObjectDataWrapper(string type, GameObject gameObject)
{
this.type = type;
this.gameObject = gameObject;
}
}
public void SyncObjects(RecievedData data)
{
//Debug.Log(JsonConvert.SerializeObject(data));
foreach (CSNC.CSNCObject obj in data.gameObjects)
{
if (inSceneGameObjects.ContainsKey(obj.id)) //if in registry update
{
Quaternion rot = new Quaternion(obj.transform.rotation[0], obj.transform.rotation[1], obj.transform.rotation[2], obj.transform.rotation[3]);
Debug.Log(JsonConvert.SerializeObject(obj.transform.position));
Vector3 pos = new Vector3(obj.transform.position[0], obj.transform.position[1], obj.transform.position[2]);
IEnumerator cor = LerpGameObjectFromFixedUpdate(inSceneGameObjects[obj.id].gameObject, rot, pos);
StartCoroutine(cor);
} else //otherwise add to registry
{
inSceneGameObjects.Add(obj.id, new GameObjectDataWrapper(obj.type,Instantiate(GameObjectRegistry.instance.registry[obj.type]))); //Dubious ahh line of code
GameObject g = inSceneGameObjects[obj.id].gameObject;
g.SetActive(true);
g.transform.position = new Vector3(obj.transform.position[0], obj.transform.position[1], obj.transform.position[2]); //these 2 might be even more dubious than the last
g.transform.rotation = new Quaternion(obj.transform.rotation[0], obj.transform.rotation[1], obj.transform.rotation[2], obj.transform.rotation[3]);
}
}
}
IEnumerator LerpGameObjectFromFixedUpdate(GameObject obj, Quaternion rot, Vector3 pos)
{
obj.transform.position = pos;
obj.transform.rotation = rot;
yield return null;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7f3dbff6bcfe57f4fb9ba717c6fecc0e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -8,7 +8,8 @@ public class GameObjectRegistry : MonoBehaviour
public static GameObjectRegistry instance; //instance of gameobject registry in scene
public string uuid; //unique identifier for this client
public List<GameObject> gameObjects; //list to make it easy to register new gameobjects
public List<string> ids;
public List<string> types;
public Dictionary<string, GameObject> registry = new Dictionary<string, GameObject>(); //registry of game objects that can be synced (needs to be identical on both clients
public List<SyncData> registeredObjects = new List<SyncData>(); //game objcets that are currently being synced
@@ -25,7 +26,7 @@ public class GameObjectRegistry : MonoBehaviour
void initializeRegistry()
{
int i = 0;
foreach (string id in ids)
foreach (string id in types)
{
registry.Add(id, gameObjects[i]);
i++;

View File

@@ -1,6 +1,6 @@
# Unity Setup
First, set the ip of the csnc compatible server.
First, set the ip of the csnc compatible server. \
(it needs to be formatted as such http://192.51.100.123:1234/ or http://mydomain.tld:1234/)
![image](./InstructionalImages/ipchange.png)
Then schedule the GameObjectRegistry script to be\

View File

@@ -6,9 +6,12 @@ public class SyncData //structure for gameobjects and data sent over to the serv
{
public SyncData(Transform pos, string type)
{
this.id = System.Guid.NewGuid().ToString();
this.transform = pos;
this.type = type;
}
public Transform transform;
public string id;
public string type; //key in registry data that defines which gameobject to instantiate with transform: position
}

View File

@@ -1,7 +1,16 @@
# CSNC
Client-Sync-Net-Code for unity game objects
Client-Sync-Net-Code for unity (but potentially not just unity) game objects
A set of very basic drop-in scripts for client-server games that allows 'p2p' style synchronization
great for synchronizing simple visual things that are mostly controlled by clients such as particles or thrown objects
## Unity Setup
Please refer to : \
https://gitea.apexfight.net/apex/CSNC/src/branch/main/Client/Unity#unity-setup
## NodeJS Setup
Email me at vlrtch3571@gmail.com if you want me to make a tutorial or need help
this project is about 90% complete other than the godot and go implementations which will be done if i ever decide to make anything with either one

View File

@@ -1,9 +1,13 @@
const timeoutTickRate = 1; //timeout check every x seconds
const timeoutLength = 5000; //timeout length in ms
var gameObjectStore = {}; //object which stores CSNCgameobjects for each client
//below is an example of the structure of gameobjectstore
/**
* {
* "2c0a48e9-40c6-4139-9697-992397773b12": [
* "2c0a48e9-40c6-4139-9697-992397773b12":
* time: Date.now(),
* gameObjects: [
* {
"type": "razorblade",
"transform": {
@@ -42,6 +46,21 @@ var gameObjectStore = {}; //object which stores CSNCgameobjects for each client
*
*/
/**
* @param {string} uuid
*/
function removePlayer(uuid) {
delete gameObjectStore[uuid];
}
function checkTimeout() {
for (let key in gameObjectStore) {
if (Date.now() - gameObjectStore[key].time > timeoutLength) {
removePlayer(key);
}
}
}
/**
* @param {http.ServerResponse<http.IncomingMessage>} res
* @param {string} req
@@ -53,9 +72,7 @@ async function handleResponse(res, req) {
var request = JSON.parse(req);
let gameObjects = request.gameObjects;
gameObjectStore[request.uuid] = gameObjects; //store gameobjects to send to other players
gameObjectStore[request.uuid] = {gameObjects: gameObjects, time: Date.now()}; //store gameobjects to send to other players
res.writeHead(200);
res.write(JSON.stringify(responseObjectHelper(request.uuid)));
@@ -68,6 +85,7 @@ async function handleResponse(res, req) {
}
}
//preps and returns data to send back to client
function responseObjectHelper(uuid) {
var responseObject = {
@@ -76,9 +94,11 @@ function responseObjectHelper(uuid) {
for (let key in gameObjectStore) {
if (key == uuid) continue; //dont send back players own data
responseObject.gameObjects = responseObject.gameObjects.concat(gameObjectStore[key]);
responseObject.gameObjects = responseObject.gameObjects.concat(gameObjectStore[key].gameObjects);
}
return responseObject;
}
setInterval(checkTimeout, timeoutTickRate * 1000);
exports.handleResponse = handleResponse;