How can I list rooms in Unity? (PUN2) - c#

I need to list the romos there are in my lobby scene. For now, this is the code I've used but I don't know why it isnt working. Is this the correct way?
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
print(roomList.Count + " Rooms");
base.OnRoomListUpdate(roomList);
foreach (var Item in roomList)
{
RoomName = Item.Name;
PlayerAmount = Item.PlayerCount;
MaxPlayers = Item.MaxPlayers;
PhotonNetwork.Instantiate("RoomPrefab", transform.position, transform.rotation);
RoomPrefab.transform.Find("RoomName").GetComponent<Text>().text = RoomName;
RoomPrefab.transform.Find("PlayerInt").GetComponent<Text>().text = PlayerAmount.ToString();
if(MaxPlayers == 4)
{
GameObject.Find("IPlayerA").GetComponent<Image>().sprite = Four;
}
else if (MaxPlayers == 2)
{
GameObject.Find("IPlayerA").GetComponent<Image>().sprite = Two;
}
else if (MaxPlayers == 3)
{
GameObject.Find("IPlayerA").GetComponent<Image>().sprite = Three;
}
RoomPrefab.transform.SetParent(ScrollView.transform, false);
}
}
I need to specify that I'm using Photon's PUN2, so GetRoomList won't work.

PhotonNetwork.GetRoomList() is gone in PUN2. You get rooms list and updates from ILobbyCallbacks.OnRoomListUpdate(List roomList) callback. You can optionally cache it, update it and clear it when needed.
Also you can check updates from PUN to PUN2 here
https://doc.photonengine.com/en-us/pun/v2/getting-started/migration-notes

The OnRoomListUpdate() method is called only when you've explicitly joined the lobby via PhotonNetwork.JoinLobby(). It's not enough just to be connected to the MasterServer as Jevgeni Geurtsen suggested, at least in PUN v2.15 it works this way for me.

Related

Script not recognizing Instance Script

I have 3 projects. 1 works but I want to refactor and use an asset from Unity store.
2nd project is the asset only with my mission system modified/merged to work with the asset and it works fine. Awesome.
So the 3rd project is the one I need to work, it is my original project with the asset imported in.
The issue I am having is the mission system, for some reason the ApplicationManager instance is not being seen inside the itempickup used like "ApplicationManager.instance" BUT it works in the 2nd project, they are literal copy/paste and no idea why it is not seen?
error CS0103: The name 'ApplicationManager' does not exist in the current context
EDIT:
It seems only the assets scripts are not seeing the ApplicationManager, I tested with my other scripts and they see it just fine, not sure what to look at to get them to work in this project since they work in the 2nd project fine.
EDIT 2: So it seems it simething about how he made the asset. If I move the AppMan to HIS folder labeled PickUp (for items) it now sees the script. How the &(* do I remove or fix that restriction?? Never seen it before. Once I move it to that folder all my other scripts now lose the reference.
Application manager:
public class ApplicationManager : MonoBehaviour
{
private static ApplicationManager _Instance = null;
public static ApplicationManager instance
{
get
{
// If we don't have an instance yet find it in the scene hierarchy
if (_Instance == null)
{
_Instance = (ApplicationManager)FindObjectOfType(typeof(ApplicationManager));
}
// Return the instance
return _Instance;
}
}
}
And the Itempickup:
public abstract class ItemPickupBase : Interactable
{
[SerializeField]
protected List<GameState> _setOnCollect = new List<GameState>();
protected void PickUpItem(ICharacter character, IItem item, bool playPickUpSound = true)
{
if (item == null)
{
Debug.LogError("Item Instance is null, can't pick up anything.");
return;
}
int addedCount = 0;
int originalCount = item.StackCount;
string rejectReason = string.Empty;
if (m_AddType == AddType.AddToMatchingContainer)
{
var containers = character.Inventory.GetContainersWithTag(item.Definition.Tag);
foreach (var container in containers)
{
if (container.GetAllowedCount(item, item.StackCount, out rejectReason) > 0)
addedCount += container.AddItem(item);
if (addedCount == originalCount)
break;
}
if (originalCount > 1)
item.StackCount = originalCount - addedCount;
}
if (addedCount != originalCount)
{
var containers = character.Inventory.Containers;
foreach (var container in containers)
{
if (container.GetAllowedCount(item, item.StackCount, out rejectReason) > 0)
addedCount += container.AddItem(item);
if (addedCount == originalCount)
break;
}
}
if (addedCount > 0)
{
string pickupUpMessage = $"Pickup up {item.Name}";
if (addedCount > 1)
pickupUpMessage += $" x {addedCount}";
MessageDisplayerUI.PushMessage(pickupUpMessage, item.Definition.Icon);
if (playPickUpSound)
m_PickUpSound.Play2D(1f, SelectionType.Random);
if (addedCount >= originalCount)
if (ApplicationManager.instance && _setOnCollect.Count > 0)
{
foreach (GameState state in _setOnCollect)
{
ApplicationManager.instance.SetGameState(state.Key, state.Value);
}
}
Destroy(gameObject);
}
else
{
MessageDisplayerUI.PushMessage(rejectReason != string.Empty ? rejectReason : "Inventory Full", Color.red);
}
}
}
Not sure what to try since this is almost a literal copy/paste from the original and it is not working. The only difference I can see is the ItemPickup in the 2nd project is a public class and the one I am having issues with is a public abstract class.
But when I try to access the ApplicationManager in another script it still does not see it so I figured that was not the issue.

nested foreach() iteration is mixing up target collections

I'm trying to write a script in space-engineers, and I have a collection of a class _Filter, which resides inside of another class _Inventory. Here is the code that I'm trying to execute:
public void InventorySetup(_Inventory inventory)
{
if (inventory.InvBlock != null) // Check if block exists
{
string[] data = inventory.InvBlock.CustomData.Split('\n'); // Break customData into lines
foreach (string nextline in data) // Iterate each line
{
if (nextline.Length > 0) // Line must contain information
{
string[] lineblocks = nextline.Split(' '); // Break each line into blocks
if (lineblocks.Length > 1) // There must be more than one block to have filter candidate and desired update
{
string[] itemID = new string[2];
if (lineblocks[0].Contains(":"))
itemID = lineblocks[0].Split(':'); // split the type from subType
else if (lineblocks[0].Contains("!"))
itemID = new string[] { "", "" };
else
itemID = new string[] { "null", "null" };
foreach (_Filter nextFilter in inventory.Filters)
{
if (ContainsCIS(nextFilter.ItemType, itemID[0]) && ContainsCIS(nextFilter.ItemSubType, itemID[1]))
{
for (int i = 1; i < lineblocks.Length; i++)
{
switch (lineblocks[i][0])
{
case '#':
nextFilter.Target = (MyFixedPoint)float.Parse(lineblocks[i].Replace('#', ' '));
break;
case '+':
if (ContainsCIS(lineblocks[i], "in"))
nextFilter.IN_BOUND = true;
if (ContainsCIS(lineblocks[i], "out"))
nextFilter.OUT_BOUND = true;
break;
case '-':
if (ContainsCIS(lineblocks[i], "in"))
// nextFilter.IN_BOUND = false;
if (ContainsCIS(lineblocks[i], "out"))
nextFilter.OUT_BOUND = false;
break;
}
}
}
}
}
if (nextline[0] == '&')
{
if (ContainsCIS(nextline, "empty"))
{
if (nextline.Contains("-"))
inventory.EMPTY = false;
else
inventory.EMPTY = true;
}
if (ContainsCIS(nextline, "fill"))
{
if (nextline.Contains("-"))
inventory.FILL = false;
else
inventory.FILL = true;
}
if (ContainsCIS(nextline, "active"))
{
if (nextline.Contains("-"))
inventory.ACTIVE = false;
else
inventory.ACTIVE = true;
}
if (ContainsCIS(nextline, "clean"))
{
if (nextline.Contains("-"))
inventory.CLEAN = false;
else
inventory.CLEAN = true;
}
if (ContainsCIS(nextline, "reset"))
inventory.Reset();
}
}
}
}
}
So basically what's going on, there's a member within the _Inventory class that refers directly to the block who's inventory I'm trying to configure. This member has it's own member called CustomData, which is a string object that can be edited in game. So I split it into an array of each line, and then process each line based on the contextual nature of each.
First, the line gets broken up into "blocks" by once again splitting each line by a space character, and then analyzing further. If the number of blocks is greater than 1, this means there is an expected "target filter" and some sort of following qualifier. Either a change to its white-list/black-list setting, or a change to the target maximum value (0 means no limit and is the default value for this member).
Now the _Inventory class already contains a pre-existing collection of _Filter class, and merely updates the members of it by means of iteration. So for example, if I want to black-list "IN_BOUND" ores, I would add the line "ore: -in". or if I want to black-list out bound steel plates, ":steelplate -out".
(a name before the colon depicts the category, and one after the colon, the specific type of item. Exclamation mark means all items in the collection). The ContainCIS() method is something I made simply to search for a contained string "Case In-Sensitively". Besides changing filters, the _Inventory class also possesses a few bool members for controlling desired functionality. They are self-named EMPTY, FILL, ACTIVE, CLEAN (the clean bool has to do with production blocks that sometimes get stuffed with the wrong materials, non-relevant to my problem)
My actual problem:
When this code is called, if I only have a single _Inventory in the root collection, everything works out fine. HOWEVER, if I have more than one, the bools are updated normally, but the filters GET UPDATED IN REVERSE ORDER.
So say for example I have _Inventory A, and _Inventory B. If I add the line to the custom data of the block that is referred to by "A", then the correct bool changes. But if I change a filter setting on "A", it updates to "B" instead.
EDIT:
Here's the classes and the method which calls the InventorySetup():
public class _Inventory
{
public IMyTerminalBlock InvBlock;
public _BlockType BlockType; // Cargo, Assembler, Refinery
public _Filter[] Filters; // Expected inventory candidates (Refer to _Filter libraries)
public bool FILL; // Attempt fill action on all IN_BOUND "true" candidates
public bool EMPTY; // Attempt empty action on all OUT_BOUND "true" candidates
public bool ACTIVE; // Actively being manipulated by program ("Use conveyor system" property will be disabled on prod. blocks)
public bool CLEAN; // Actively clear overburdened assembler inputs, or un-scheduled refinery inputs (not used for cargos)
public _Inventory(IMyTerminalBlock invBlock, _BlockType blockType, _Filter[] filters, bool active = true)
{
InvBlock = invBlock;
BlockType = blockType;
Filters = filters;
FILL = false;
EMPTY = false;
ACTIVE = active;
CLEAN = true;
}
public void Reset()
{
foreach (_Filter nextFilter in Filters)
{
nextFilter.Target = 0;
nextFilter.IN_BOUND = true;
nextFilter.OUT_BOUND = true;
}
}
}
public class _Filter
{
public string ItemType;
public string ItemSubType;
public MyFixedPoint Target;
public int Priority;
public bool IN_BOUND;
public bool OUT_BOUND;
public _Filter(string itemType = "null")
{
ItemType = itemType.Split(':')[0];
ItemSubType = itemType.Split(':')[1];
Target = 0; // 0 means no target value, any amount aloud
Priority = 0; // Priority for in-bound requests (WIP)
IN_BOUND = true; // Default to whitelist items in & out of inventory
OUT_BOUND = true;
}
}
public void InventoryListUpdate()
{
Inventories.RemoveAll(x => x.InvBlock == null);
Inventories.RemoveAll(x => !x.InvBlock.CustomName.Contains(Signature));
foreach (IMyCargoContainer nextCargo in Cargos)
{
if (Inventories.FindIndex(x => x.InvBlock == nextCargo) < 0 && nextCargo.CustomName.Contains(Signature))
Inventories.Add(new _Inventory((IMyTerminalBlock)nextCargo, _BlockType.CARGO, FullLibrary));
}
foreach (IMyRefinery nextRefinery in Refineries)
{
if (Inventories.FindIndex(x => x.InvBlock == nextRefinery) < 0 && nextRefinery.CustomName.Contains(Signature))
Inventories.Add(new _Inventory((IMyTerminalBlock)nextRefinery, _BlockType.ASSEMBLER, RefineryLibrary));
}
foreach (IMyAssembler nextAssembler in Assemblers)
{
if (Inventories.FindIndex(x => x.InvBlock == nextAssembler) < 0 && nextAssembler.CustomName.Contains(Signature))
Inventories.Add(new _Inventory((IMyTerminalBlock)nextAssembler, _BlockType.ASSEMBLER, AssembleLibrary, false));
}
foreach (_Inventory nextInventory in Inventories)
{
if (bInventoryRunning && nextInventory.ACTIVE)
InventoryUpdate(nextInventory);
else
InventorySetup(nextInventory);
}
}
So when I started up the code in space engineers today, it suddenly started to work as intended... I have absolutely no idea how or why. I'm leaning towards a memory leak issue related to how the game handles its in-game scripts, however there is the slight possibility that cleaning the code up somehow "massaged" it to do as intended. As I stated before, it was behaving as expected to some degree, it just had un-expected behaviour as the higher collection grew in size. Since each _Inventory was being worked on individually, there should have been no way for its members to get transposed onto another, unless some weird GC-voodoo was happening in the background. As comforting as it is to have my script running properly, it's an even worse feeling knowing I may never find out why it was messing up in the first place, as I have no real solid grounding from which to avoid this problem in the future. Thankyou all for your input.
I know it's been awhile since I posted this, but I feel like I have a responsibility to bring further conclusions to the issue I was having. I only just realized that the problem revolved around a particular class array that I had added to the container class. The _Filter[] within _Inventory, had been passed by reference, rather than being "cloned", something I have just recently become aware of, so every time each individual _Inventory object was being iterated through, their contained arrays were all pointing to the same thing. I have managed to find a way to properly clone each element from the original libraries as needed.

How can i filter Prepare Physical Count

Hello there i tried filtering the prepare physical count grid view but im not sure if im doing the correct process on this one. i added
#region UsrRemoveZero
[PXBool]
[PXUIField(DisplayName = "Remove Locations with Zero Qty")]
public bool? UsrRemoveZero { get; set; }
public abstract class usrRemoveZero : PX.Data.BQL.BqlBool.Field<usrRemoveZero> { }
#endregion
i want to filter out book qty that dont have value > 0 but i am not successful this is where i am at so far.
protected virtual IEnumerable PreliminaryResultRecs()
{
//PIGeneratorSettings filterrows = Base.GeneratorSettings.Current;
//var filterrowsExt = PXCache<PIGeneratorSettings>.GetExtension<PIGeneratorSettingsExt>(filterrows);
//foreach (PIPreliminaryResult row in Base.PreliminaryResultRecs.Select())
//{
// if (filterrowsExt.UsrRemoveZero == true)
// {
// if (row.BookQty > 0)
// yield return row;
// }
//}
foreach (PIPreliminaryResult res in PXSelect<PIPreliminaryResult>.Select())
{
// Additional restriction goes here
yield return res;
}
}
but nothing is working on my end it just error out on the screen.
You need to first expose the PXFilterable view in the extension.
I made a quick guide below that explains the pattern. I know there is other ways to do this but this works for me:
How can I filter records for the Generate Recurring Entries (GL504000) screen

PUN 2 Getting Custom Properties

I've recently taken on the task of custom properties in Photon. I have been able to figure out how to set the custom properties, but not get the custom properties. My hashtable is in my player controller script, while the place where I set (and where I want to get) properties is in a round loop script.
From RoundSystem:
private IEnumerator TeamBalance()
{
angelCount = Mathf.Floor(PhotonNetwork.PlayerList.Length * angelPercent);
currentAngels = angelCount;
currentPlayers = PhotonNetwork.PlayerList.Length;
foreach (var item in PhotonNetwork.PlayerList)
{
var itemPhotonView = (PhotonView)item.TagObject;
itemPhotonView.RPC("SetPlayerTeam", item, citiString);
}
for (int i = 0; i < angelCount;)
{
var item = PhotonNetwork.PlayerList[Random.Range(0, PhotonNetwork.PlayerList.Length)];
var itemPhotonView = (PhotonView)item.TagObject;
if (/* random player selected's, AKA, item's team == citiString */)
{
itemPhotonView.RPC("SetPlayerTeam", item, angelString);
i++;
}
}
yield return null;
//the reason this is in an IEnumerator with 'yield return null'
//is because I plan to add a waiting period once I figure this out
//it's for the game loop
}
From PlayerController:
[PunRPC]
public void SetPlayerTeam(string teamString)
{
//in the class: private ExitGames.Client.Photon.Hashtable playerProperties;
if (!playerProperties.ContainsKey("team"))
{
playerProperties.Add("team", teamString);
}
playerProperties["team"] = teamString;
PhotonNetwork.LocalPlayer.SetCustomProperties(playerProperties);
}
At the beginning of the round, a percentage (in this case 1/3) of players are chosen to be an "angel". The check here is needed because in cases of multiple angels, you don't want an already existing angel to count as a new change. (Also, it's probably important to known generally how to get custom properties if I'm going to be using them.) If I don't include the check in RoundSystem, the outcome is 2 citizens and 1 angel (in a test with 3 players). Also, if you see any spaghetti code that could be improved on, please don't hesitate to tell me. :)
Use Player.CustomProperties dictionary to access player's custom properties.
foreach (var item in PhotonNetwork.PlayerList)
{
if (item.CustomProperties.ContainsKey("team"))
{
Debug.Log(item.CustomProperties["team"]);
}
}
Also, the RoundSystem can implement IInRoomCallbacks interface and listen to OnPlayerPropertiesUpdate to catch the exact moment when the team gets updated. https://doc-api.photonengine.com/en/pun/v2/interface_photon_1_1_realtime_1_1_i_in_room_callbacks.html

How can I separate the pins I am receiving from my data into different categories?

So I need two separate the pins into groups because when I filter them I need to know which pins belongs to which category.
This is how I recieve them. So there is one group that loads if the int app.value1 is 0 and the other one is if app.value2 is 0.
The reason I need to separate them is because I am doing a filter where I need to separate the two. Should I do two different lists maybe? Or is there a better way?
List<Pin> myPins = new List<Pin>();
private async Task<List<Pin>> LoadData()
{
var pins = new List<Pin> ();
var getItems = await phpApi.getInfo ();
foreach (var currentItem in getItems["results"]) {
longString = currentItem ["long"].ToString ();
latString = currentItem ["lat"].ToString ();
if (App.value1 == 0) { //so this below is the first group of pins then
var pin = new Pin ();
pin.Position = new Position (latString, longString);
pin.Label = "testlabel";
pin.Address = "testadress";
pins.Add (pin);
}
if (App.value2 == 0) { //second group of pins
//i add pins here as well (so the same code as App.value1), but this intvalue is connected to a different "button"
pins.Add (pin);
}
}
}
And this is my filter that is not working right now because the pin doesnt know which group the belong to:
private async void FilterPins (string filter)
{
map.Pins.Clear ();
foreach(Pin p in myPins) {
if (string.IsNullOrWhiteSpace(filter) || (p.Label.Contains(filter))) {
map.Pins.Add (p); //this is just a searchfilter via my searchbar that isnt working when I add the code below. If i remove the code below this function works.
}
if (App.value1 == 0 ) {
map.Pins.Add (p);
System.Diagnostics.Debug.WriteLine ("add");
}
if (App.value2 == 0 ) {
map.Pins.Add (p);
System.Diagnostics.Debug.WriteLine ("add");
}
}
}
Since the Pin class is sealed(https://developer.xamarin.com/api/type/Xamarin.Forms.Maps.Pin/), it's a bit hard to work around. However you should be able to create two separate List<Pin> in which you can place the respective Pin objects into based on your filter. You can then swap your respective Map.Pins object with which list of Pin objects you need to display.
https://developer.xamarin.com/api/property/Xamarin.Forms.Maps.Map.Pins/
Otherwise you can create another POCO object that contains a Pin object and a string Category in it.
EX:
public class PinWithCategory
{
public Pin Pin {get; set;}
public string Category {get; set;}
}

Categories