- Home /
Networking woes - Client causing Server to "Reset" upon Loading a Level
So let me start this off by saying that my Unity Networking skills are completely horrible, and I am basically just trying to get my multiplayer up and running so I can continue with some other features I am working on that are kind of dependent on multiplayer working.
Quick summary of what I am trying to accomplish, what should happen, and what does happen:
Someone goes through my Main Menu and clicks "Multiplayer" and then "Host a Server", which initializes the server with the MasterServer and launches it, putting the player in the game (Loading a new Scene)
This player becomes the Server, at which point it spawns 8 prefabs and randomizes a bunch of data (Int and Strings) about each of these prefabs and populates a CharacterData.js script on each of the objects with the data that was randomized earlier. It also spawns this player's character, which is another Prefab with movement/camera/etc. data.
Another player goes through the Main Menu and clicks "Refresh List", selects the server he would like to join, and hits "Connect", where he is then Network.Connect()'ed to the server he selected.
Now this is where the divergence happens.
What SHOULD happen is: The Client spawns all 8 of the NPC objects as well as the other players in the server. The Server then sends the Client the location data of these NPC objects/player characters, as well as the CharacterData.js information. The Client reads this information and then populates the objects itself (To cut down on bandwidth). It should then also spawn this player's character and set control to this player.
What ACTUALLY happens is: The Server, for some reason, seems to "reset" (Calls Start(), Awake(), etc. all over again) and spawns 8 more NPCs, as well as all currently connected players again, resulting in 16+ NPCs, 3 player characters (Because the server had one before, and then spawned new ones for it and the client). However the odd part is that these new objects are ONLY seen on the Client. The Server still only sees the 8 NPCs from the start and his own character, and nothing else.
If anybody could help me, that would be amazing. Below are a bunch of the necessary code:
serverList.js
import System.Collections.Generic;
var buttonClicked: int;
private var serverListingObject: GameObject;
private var serverListingBackground: Transform;
private var serverListBeenCalled: boolean;
private var serverList = new List.<GameObject>();
static var selectedServer: HostData;
function Awake() {
requestServerListings();
DontDestroyOnLoad(this);
}
function Start() {
serverListingObject = GameObject.Find('Server Listing Template');
serverListingBackground = GameObject.Find('Server List BG').transform;
}
//DISPLAYS EACH SERVER AS A "ROW" ON A BACKGROUND, WHICH THE USER CAN CLICK AND JOIN.
function OnGUI() {
if (!serverListBeenCalled) {
var data: HostData[] = MasterServer.PollHostList();
var xPosition: float = 1.52;
var yPosition: float = -0.15;
for (var i=0;i<data.Length;i++) {
yPosition -= 0.25;
var serverName = data[i].gameName+' '+data[i].connectedPlayers+' / '+data[i].playerLimit;
var thisListingObj = Instantiate(serverListingObject, Vector3(0.08,-0.2,-0.1), Quaternion.identity);
thisListingObj.transform.parent = serverListingBackground;
thisListingObj.transform.localPosition = Vector3(xPosition,yPosition,-0.1);
serverList.Add(thisListingObj);
//SET THIS SERVER LISTING'S DATA
thisListingObj.GetComponent(ServerListing).listingServerName.GetComponent(tk2dTextMesh).text = data[i].gameName;
thisListingObj.GetComponent(ServerListing).listingServerPop.GetComponent(tk2dTextMesh).text = data[i].connectedPlayers+' / '+data[i].playerLimit;
thisListingObj.GetComponent(ServerListing).listingServerHostData = data[i];
};
serverListBeenCalled = true;
}
}
//EACH ROW IS STORED IN A LIST, WHICH IS WIPED EVERYTIME THE PLAYER REFRESHES THE SERVER LISTING
function requestServerListings() {
MasterServer.RequestHostList("MyTestProjectGame");
serverListBeenCalled = false;
for (var d=0;d<serverList.Count;d++) {
Destroy(serverList[d]);
};
serverList.Clear();
}
private var lastLevelPrefix = 0;
@RPC
function LoadLevel(level: int, levelPrefix: int) {
// There is no reason to send any more data over the network on the default channel,
// because we are about to load the level, thus all those objects will get deleted anyway
Network.SetSendingEnabled(0, false);
// We need to stop receiving because first the level must be loaded first.
// Once the level is loaded, rpc's and other state update attached to objects in the level are allowed to fire
Network.isMessageQueueRunning = false;
// All network views loaded from a level will get a prefix into their NetworkViewID.
// This will prevent old updates from clients leaking into a newly created scene.
Network.SetLevelPrefix(levelPrefix);
Application.LoadLevel(level);
yield;
yield;
// Allow receiving data again
Network.isMessageQueueRunning = true;
// Now the level has been loaded and we can start sending out data to clients
Network.SetSendingEnabled(0, true);
/*
for (var go in FindObjectsOfType(GameObject))
go.SendMessage("OnNetworkLoadedLevel", SendMessageOptions.DontRequireReceiver);*/
}
function OnDisconnectedFromServer () {
Application.LoadLevel(0); //LOAD THE MAIN MENU FOR NOW
}
function OnServerInitialized() {
Network.RemoveRPCsInGroup(0);
Network.RemoveRPCsInGroup(1);
networkView.RPC( "LoadLevel", RPCMode.AllBuffered, 3, lastLevelPrefix + 1);
}
function OnConnectedToServer() {
Network.RemoveRPCsInGroup(0);
Network.RemoveRPCsInGroup(1);
networkView.RPC( "LoadLevel", RPCMode.AllBuffered, 3, lastLevelPrefix + 1);
}
function HostServer() {
Network.InitializeServer(7, Random.Range(25000,30000), !Network.HavePublicAddress());
MasterServer.RegisterHost("MyTestProjectGame", "Test Game #"+Random.Range(1,11), "Please work.");
}
function ConnectToServer() {
Network.Connect(selectedServer); //CONNECT USING THE HOSTDATA OF THE SELECTED SERVER LISTING ROW
}
function RefreshListings() {
Debug.Log('Refresh the Server Listings');
requestServerListings();
}
This one handles Refreshing the list, display the list, hosting a server, and joining a server. Most of the code I found across the internet, as my Networking skills, like I said, are not that good. The last 3 functions are called through .SendMessage()'s from 3 buttons that are "Host a Server", "Connect", and "Refresh".
characterGenerator.js
function Awake() {
if (Network.isServer) { //ONLY THE SERVER HANDLES THE XML FILES
LoadAllXMLFiles(); // LOAD & CACHE ALL XML FILES BEFORE ANYTHING ELSE IS DONE (NOT SHOWN HERE BECAUSE IT DOES NOT PERTAIN TO THIS PROBLEM)
}
}
//START() AND AWAKE() ARE BOTH CALLED ONCE WHEN THE SERVER IS SETUP, AND THEN AGAIN WHENEVER A NEW PLAYER CONNECTS. TO MY KNOWLEDGE IT SHOULD ONLY BE GETTING CALLED ONCE, ESPECIALLY SINCE IT SPECIFICALLY ASKS FOR ISSERVER.
function Start() {
if (Network.isServer) {
for (var i=0;i<8;i++) {
var thisCharacter = Network.Instantiate(characterTemplate, Vector3(Random.Range(-15, 15), 0, 0), Quaternion.identity, 0);
generateInfo(thisCharacter, false);
};
}
if (networkView.isMine) {
spawnOwnCharacter();
}
}
function spawnOwnCharacter() {
var thisPlayerCharacter = Network.Instantiate(playerCharTemplate, Vector3(Random.Range(-15, 15), 0, 0), Quaternion.identity, 0);
generateInfo(thisPlayerCharacter, true);
}
/*
THE FOLLOWING GENERATEINFO() FUNCTION HAS A LOT MORE IN IT THAN IT HAS HERE, BUT IT DOES NOT PERTAIN TO THIS PROBLEM AS IT'S JUST PULLING DATA FROM AN XML AND PARSING IT. ALL THE VARIABLES IN THE SETCHARDATA FUNCTION ARE STRINGS AND INTS.
*/
function generateInfo() {
if (Network.isServer) {
setCharData(charName, charAge, charNationality, charHometown, charOccupation, charHistory, charReason, charGender);
}
}
@RPC
function setCharData(charName: String, charAge: int, charNationality: int, charHometown: String, charOccupation: String, charHistory: String, charReason: String, charGender: int) {
thisCharacterDataScript.charName = charName;
thisCharacterDataScript.charAge = charAge;
thisCharacterDataScript.charNationality = charNationality;
thisCharacterDataScript.charHometown = charHometown;
thisCharacterDataScript.charOccupation = charOccupation;
thisCharacterDataScript.charHistory = charHistory;
thisCharacterDataScript.charReason = charReason;
thisCharacterDataScript.charGender = charGender;
if (networkView.isMine) {
networkView.RPC('setCharData', RPCMode.OthersBuffered, charName, charAge, charNationality, charHometown, charOccupation, charHistory, charReason, charGender);
}
}
This one handles all the generation and passing of data from the Server to the Client, or at least I hope one day it does.
If anybody can make heads or tails of this, I would be forever in your debt. Unity Networking is NOT my strong suit, I have spent all day working on this and all I've gotten is a headache and a MonoDevelop spaghetti of garbled code.
Thanks, DarKTower
Answer by DarKTower · Jun 05, 2014 at 07:15 AM
Fixed this myself. I moved most of the spawning code from CharacterGenerator.js into the ServerList.js, removed the two Levels (Which removed the entire loading sequence which was causing some de-sync issues I believe) and combined the Server List into the Game Scene, and just have the Camera move between the two when the player connects.
This isn't really an actual fix, just masking the problem. How could I actually implement player spawning in new scenes?
Your answer
Follow this Question
Related Questions
unity c# RPC 0 Answers
supplied parameters doesn't match the rpc declaration 1 Answer
RPC setName() not working correctly 0 Answers
Networking initial questions 0 Answers
Using RPC over diffrent projects? 1 Answer