Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by Hez · Nov 12, 2015 at 11:01 PM · c#minecraftblocks

C# Better block world loading, generating and saveing

I have been working on this game for a few weeks, and I have gotten flat world generation, world save and world loading working, but it is slow. Moving and loading chunks will pause the game with lag, as will saving chunks. I am wondering if anyone is capable and willing to help, maybe I am doing things the slow way? maybe this would work on a better and faster computer then mine.

This is meant to be a rpg style game, so not too much focus is going to be put on building, but I still want some save-ability and world creation. I want people to make custom maps and share them.

sorry for the mess of code

This is in my move script

     public static void CheckChunks (){
 
         Vector3 PosDis = Vector3.zero;
         
         
         while (PosDis.y < 3){
             while (PosDis.z < 3){
                 while (PosDis.x < 3){
 
                     P = PlayerChunk + PosDis;
                     N = PlayerChunk - PosDis;
 
                     if (N.y < 0){N.y = 0;}
                     
                     string[] ChunkName = new string[]{
                         
                         P.x.ToString() + "." + P.y.ToString() + "." + P.z.ToString() + ".",
                         P.x.ToString() + "." + P.y.ToString() + "." + N.z.ToString() + ".",
                         P.x.ToString() + "." + N.y.ToString() + "." + P.z.ToString() + ".",
                         P.x.ToString() + "." + N.y.ToString() + "." + N.z.ToString() + ".",
                         N.x.ToString() + "." + P.y.ToString() + "." + P.z.ToString() + ".",
                         N.x.ToString() + "." + P.y.ToString() + "." + N.z.ToString() + ".",
                         N.x.ToString() + "." + N.y.ToString() + "." + P.z.ToString() + ".",
                         N.x.ToString() + "." + N.y.ToString() + "." + N.z.ToString() + "."
                         
                     };
 
                     CheckChunksNum = new Vector3[]{
                         new Vector3(P.x , P.y , P.z) ,
                         new Vector3(P.x , P.y , N.z) ,
                         new Vector3(P.x , N.y , P.z) ,
                         new Vector3(P.x , N.y , N.z) ,
                         new Vector3(N.x , P.y , P.z) ,
                         new Vector3(N.x , P.y , N.z) ,
                         new Vector3(N.x , N.y , P.z) ,
                         new Vector3(N.x , N.y , N.z) 
 
                     };
                     int count = 0;
                     while (count < 7){
                         if (!GameCore.ChunkLoaded.Contains (ChunkName[count])) {
                             Chunks.StartChunk(CheckChunksNum[count].x,CheckChunksNum[count].y,CheckChunksNum[count].z);
 
                         }
                         count++;
                     }
                     
                     PosDis.x += 1;
                     
                 }
                 PosDis.x = 0;
                 PosDis.z += 1;
             }
             PosDis.x = 0;
             PosDis.z = 0;
             PosDis.y += 1;
             
         }
     }


this in the move script update

         TimeToCheck += 1 * Time.deltaTime;
             if (TimeToCheck > 0.005f) {
 
 
                 if (transform.position.y > 0) {
                     CheckChunks ();
                     TimeToCheck = 0;
                 }
             }


this is the whole of my chunk loading and generating script

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 
 public class Chunks : MonoBehaviour {
         
     
     public static string[] SortChunk;
     public static string[] SortBlocks;
     public static string[,] HoldChunks = new string[196,14];
     public static  string[,]  ThisBlock = new string[196,14];
     public static Vector3 ChunkNumbers = Vector3.zero;
     public static GameObject ThisObject;
 
     // Use this for initialization
     void Start () {
         LoadChunk();
         ThisObject = gameObject;
     }
     
     public void LoadChunk (){
         
         float X14Sect = Mathf.Floor(GameCore.PlayerLocation.x / 14);
         float Y14Sect = 0;
         float Z14Sect = Mathf.Floor(GameCore.PlayerLocation.z / 14);
         
         StartChunk(X14Sect,Y14Sect,Z14Sect);
         
         
         Vector3 AddToSect = Vector3.zero;
         bool OutAddToSect = false;
         float Switched = 0;
         float MultiplyerX = 1;
         float MultiplyerZ = 1;
         
         
         while (!OutAddToSect) {
             
             while (Switched <= 2){
                 while (AddToSect.x != (4 * MultiplyerX)) {
                     StartChunk (X14Sect + AddToSect.x, Y14Sect + AddToSect.y, Z14Sect + AddToSect.z);
                     AddToSect.x += MultiplyerX;
                 }
                 AddToSect.x = 0;
                 Switched += 1;
                 MultiplyerX *= -1;
             }
             
             Switched = 0;
             MultiplyerX = 1;
             MultiplyerZ = 1;
             AddToSect = Vector3.zero;
             
             while (Switched <= 2){
                 while (AddToSect.x != (4 * MultiplyerX)){                
                     while (AddToSect.y <= 7) {
                         StartChunk (X14Sect + AddToSect.x, Y14Sect + AddToSect.y, Z14Sect + AddToSect.z);
                         AddToSect.y += 1;
                     }
                     AddToSect.y = 0;
                     AddToSect.x += MultiplyerX;
                 }
                 AddToSect.y = 0;
                 AddToSect.x = 0;
                 Switched += 1;
                 MultiplyerX *= -1;
             }
             
             Switched = 0;
             MultiplyerX = 1;
             MultiplyerZ = 1;
             AddToSect = Vector3.zero;
             
             while (Switched <= 3){
                 while (AddToSect.x != (4 * MultiplyerX)){                
                     while (AddToSect.y <= 7) {
                         while (AddToSect.z != (4 * MultiplyerZ)) {
                             StartChunk (X14Sect + AddToSect.x, Y14Sect + AddToSect.y, Z14Sect + AddToSect.z);
                             AddToSect.z += MultiplyerZ;
                         }
                         AddToSect.z = 0;
                         AddToSect.y += 1;
                     }
                     AddToSect.z = 0;
                     AddToSect.y = 0;
                     AddToSect.x += MultiplyerX;
                 }
                 if (MultiplyerX == -1){ MultiplyerZ = -1; }
                 AddToSect.z = 0;
                 AddToSect.y = 0;
                 AddToSect.x = 0;
                 Switched += 1;
                 MultiplyerX *= -1;
             }
             
             AddToSect = Vector3.zero;
             OutAddToSect = true;
             //end while
         }
         
         
         
     }
     
     public static void StartChunk (float ChunkNumberx, float ChunkNumbery, float ChunkNumberz){
         string ChunkName = ChunkNumberx.ToString () + "." + ChunkNumbery.ToString () + "." + ChunkNumberz.ToString ();
         ChunkNumbers = new Vector3 (ChunkNumberx,ChunkNumbery,ChunkNumberz);
         if (!GameCore.ChunkLoaded.Contains (ChunkName)) {
             
 
             bool SkipStart = false;
             
             
             try {
                 
                 float SetX = ChunkNumberx * 14;
                 float SetY = ChunkNumbery * 14;
                 float SetZ = ChunkNumberz * 14;
                 string text = "";
                 try {
 
 
                     text = System.IO.File.ReadAllText ( Core.StaticWorldSaveLocation + @"\Dem\Soul\Land\" + ChunkNumberx.ToString () + "." + ChunkNumbery.ToString () + "." + ChunkNumberz.ToString () + ".dat");
                     
                 }
                 catch
                 {
                     GenChunck(ChunkNumbers);
                     SkipStart = true;
                 }
                 
                 if (!SkipStart){
                     GameCore.ChunkLoaded.Add (ChunkName);
                     GameObject ChunckCoreObject;
                     
                     SortChunk = text.Split ('-');
                     
                     ChunckCoreObject = (GameObject)Instantiate(Core.StaticChunk, new Vector3 (ChunkNumberx * 7f,ChunkNumbery * 7f,ChunkNumberz * 7f), Quaternion.Euler (0,0,0));
                     ChunckCoreObject.name = ChunkName;
                     ChunckCoreObject.GetComponent <ChunkCore> ().ChunkNumber = new Vector3 (ChunkNumberx, ChunkNumbery, ChunkNumberz);
                     
                     int SortTrys = 0;
                     
                     while (SortTrys < 14) {
                         
                         int setChunkDat = 0;
                         SortBlocks = SortChunk [SortTrys].Split (',');
                         
                         while (setChunkDat < 196) {
                             
                             HoldChunks [setChunkDat, SortTrys] = SortBlocks [setChunkDat];
                             setChunkDat += 1;
                         }
                         
                         SortTrys += 1;
                         //end setting chunk
                     }
                     
                     ThisBlock = (HoldChunks);
                     
                     int blockcount;
                     SortTrys = 0;
                     while (SortTrys < 14) {
                         int setChunkDatInt = 0;
                         
                         while (setChunkDatInt < 196) {
                             
                             if (int.Parse (ThisBlock [setChunkDatInt, SortTrys]) > 0) {
                                 int xR = Random.Range (0, 4);
                                 int yR = Random.Range (0, 4);
                                 int zR = Random.Range (0, 4);
                                 int RowNum = setChunkDatInt / 14;
                                 int CollomNum = 14 * RowNum;
                                 GameObject Block = (GameObject)Instantiate (Core.StaticBricks [int.Parse (ThisBlock [setChunkDatInt, SortTrys])], new Vector3 (((setChunkDatInt - CollomNum) + SetX) * 0.5f, (SortTrys + SetY) * 0.5f, (RowNum + SetZ) * 0.5f), Quaternion.Euler (90 * xR, 90 * yR, 90 * zR));
                                 Block.GetComponent <CubeCore> ().CubeChunkCore = ChunckCoreObject;
                                 Block.GetComponent <CubeCore> ().CubeChunk = ChunkName;
                                 Block.GetComponent <CubeCore> ().FromLoad = true;
                                 Block.GetComponent <CubeCore> ().CubeLine = new Vector2 (setChunkDatInt, SortTrys);
                                 ChunckCoreObject.GetComponent <ChunkCore> ().ChunkBlocks[setChunkDatInt, SortTrys] = ThisBlock [setChunkDatInt, SortTrys];
                                 ChunckCoreObject.GetComponent <ChunkCore> ().CubeList[setChunkDatInt, SortTrys] = Block.gameObject;
                             }
                             else{
                                 ChunckCoreObject.GetComponent <ChunkCore> ().ChunkBlocks[setChunkDatInt, SortTrys] = "0";
                                 
                             }
                             
                             setChunkDatInt += 1;
                             
                         }
                         SortTrys += 1;
                         //end spawning blocks
                     }
                 }
             } catch {GenChunck(ChunkNumbers);
             }
         } 
     }
 
     public static void GenChunck(Vector3 ChunkNumber){
         
         int counter = 0;
         int countery = 0;
         bool CanSave = false;
         string[,] ChunkBlocks = new string[196,14];;
     
         if (ChunkNumber.y < 1) {
             while (counter<196) {
             
                 ChunkBlocks [counter, 0] = "1";
             
                 counter++;
             }
             countery = 1;
             while (countery < 14) {
                 counter = 0;
                 while (counter<196) {
                     ChunkBlocks [counter, countery] = "0";
                     counter++;
                 }
                 countery++;
             }
         
         } 
     
         if (ChunkNumber.y > 0) {
             while (countery < 14) {
                 counter = 0;
                 while (counter<196) {
                     ChunkBlocks [counter, countery] = "0";
                 
                     counter++;
                 }
                 countery++;
             }
         }
                         
         string SaveBlocks = "";
         int SortTrys = 0;
         
         while (SortTrys < 14 ) {
             
             int setChunkDat = 0;
             
             while (setChunkDat < 195) {
                 
                 
                 SaveBlocks += ChunkBlocks[setChunkDat,SortTrys] + ",";
                 setChunkDat += 1;
             }
             SaveBlocks += ChunkBlocks[setChunkDat,SortTrys] + "-";
             SortTrys += 1;
             //end setting chunk
         }
 
         System.IO.File.WriteAllText( Core.StaticWorldSaveLocation + @"\Dem\Soul\Land\" + ChunkNumber.x + "." + ChunkNumber.y + "." + ChunkNumber.z + ".dat", SaveBlocks);
 
         StartChunk (ChunkNumber.x,ChunkNumber.y,ChunkNumber.z);
 
     }
     
     // Update is called once per frame
     void Update () {
         
         
         
         
         //end update
     }
 }
 

this is my chunk registration script

 using UnityEngine;
 using System.Collections;
 
 
 
 public class ChunkCore : MonoBehaviour {
 
     public string[,] ChunkBlocks = new string[196,14];
     public Vector3 ChunkNumber;
     public GameObject[,] CubeList = new GameObject[196,14]; 
 
 
 
     // Use this for initialization
     void Start () {
 
     }
 
 
 
 
 
     public void SaveChunk(){
 
         string SaveBlocks = "";
         int SortTrys = 0;
         
         while (SortTrys < 14 ) {
             
             int setChunkDat = 0;
             
             while (setChunkDat < 196) {
                  
 
                 SaveBlocks += ChunkBlocks[setChunkDat,SortTrys] + ",";
                 setChunkDat += 1;
             }
             SaveBlocks += "-";
             SortTrys += 1;
             //end setting chunk
         }
 
           System.IO.File.WriteAllText( Core.StaticWorldSaveLocation + @"\Dem\Soul\Land\" + transform.name + ".dat", SaveBlocks);
 
 
     
     }
 
     float DelayChunkGen = 0;
 
     // Update is called once per frame
     void Update () {
 
         DelayChunkGen += 1 * Time.deltaTime;
 
         if (DelayChunkGen > 1.5) {
 
         }
 
     }
 }
 

this is my cube registration script

 using UnityEngine;
 using System.Collections;
 
 public class CubeCore : MonoBehaviour {
     
     public string CubeName;
     public bool FromLoad;
     
     public float CubeNumber;
     public float CubeWeight;
     public float CubeBurn;
     public float CubeBreak;
     public string CubeChunk;
     public Vector2 CubeLine;
     public GameObject CubeChunkCore;
     
     static public string staticCubeName;
     
     static public float staticCubeNumber;
     static public float staticCubeWeight;
     static public float staticCubeBurn;
     static public float staticCubeBreak;
     
     
     // Use this for initialization
     void Start () {
         staticCubeName = CubeName;
         staticCubeNumber = CubeNumber;
         staticCubeWeight = CubeWeight;
         staticCubeBurn = CubeBurn;
         staticCubeBreak = CubeBreak;
         //try{
 
         
         if (!FromLoad){
             try{
             
             
             Vector3 ChunkName = new Vector3 (Mathf.Floor((transform.position.x )/ 7f) , Mathf.Floor(transform.position.y / 7f), Mathf.Floor(transform.position.z / 7f));
             CubeChunk =    ChunkName.x.ToString() + "." + ChunkName.y.ToString() + "." + ChunkName.z.ToString();
             CubeChunkCore = GameObject.Find(CubeChunk);
 
                 CubeLine = new Vector2 (Mathf.Floor((Mathf.Abs(CubeChunkCore.transform.position.x - transform.position.x) + (Mathf.Abs(CubeChunkCore.transform.position.z - (transform.position.z))*14))/0.5f), Mathf.Floor(Mathf.Abs(CubeChunkCore.transform.position.y - transform.position.y)/0.5f));
             if (!GameCore.ChunkLoaded.Contains(CubeChunk)){
                 HardBreak();}    
 
             if (CubeChunkCore.GetComponent <ChunkCore> ().ChunkBlocks[(int) CubeLine.x,(int) CubeLine.y] == "0"){
                 
                 try {
                     CubeChunkCore.GetComponent <ChunkCore> ().ChunkBlocks[(int) CubeLine.x,(int) CubeLine.y] = CubeNumber.ToString();
                     CubeChunkCore.GetComponent <ChunkCore> ().CubeList[(int) CubeLine.x,(int) CubeLine.y] = gameObject;
                 }catch{}
                 
                 
                 if (!GameCore.SaveChunks.Contains(CubeChunkCore)){
                     GameCore.SaveChunks.Add(CubeChunkCore);
                 }
             } else{
                 HardBreak();
             }
 
             }catch{HardBreak();}
         }
         
     }
     
     
     public void Break(bool IsDroping){
         if (CubeNumber > 2){
         CubeChunkCore.GetComponent <ChunkCore> ().ChunkBlocks[(int) CubeLine.x,(int) CubeLine.y] = "0";
             Destroy (gameObject);
             
             if (!GameCore.SaveChunks.Contains(CubeChunkCore)){
                 GameCore.SaveChunks.Add(CubeChunkCore);
             }
         }
     }
     public void HardBreak(){
         Destroy (gameObject);
         //CubeChunkCore.GetComponent <ChunkCore> ().ChunkBlocks[(int) CubeLine.x,(int) CubeLine.y] = "0";
         if (!GameCore.SaveChunks.Contains(CubeChunkCore)){
             GameCore.SaveChunks.Add(CubeChunkCore);
         }
     }
 
     public void OverRide(){
         CubeChunkCore.GetComponent <ChunkCore> ().ChunkBlocks[(int) CubeLine.x,(int) CubeLine.y] = "0";
     }
     // Update is called once per frame
     void Update () {
         
     }
 }
 

game core script

 using UnityEngine;
 using System.Collections;
 using System.Collections.Generic;
 
 public class GameCore : MonoBehaviour {
 
     public static Vector3 PlayerLocation = new Vector3 (1,10f,3);
     public static List<string> ChunkLoaded = new List<string> ();
     public static float VewingDis = 5;
     public static int StaticSeed;
     public static float BrushSize = 1;
     public static float PlayerHight = 1.708f;
     public static GameObject PlayerObject;
 
     public int Seed = 500;
     public float GameTimerSave = 0;
     public float SaveTime = 2;
     static public List<GameObject> SaveChunks = new List<GameObject>();
     static public bool RunSaveAll = false;
 
     void Start () {
 
         StaticSeed = Seed;
 
     }
     
 
     void Update () {
         GameTimerSave += 1 * Time.deltaTime;
     
         if (RunSaveAll) {
             SaveAll();
             RunSaveAll = false;
         }
 
         if (GameTimerSave >= SaveTime) {
             SaveAll();
             GameTimerSave = 0;
         }
 
     }
 
 
 
     public static void SaveAll(){
         try{
             foreach (GameObject UnsavedChuncks in SaveChunks)
             {
                 UnsavedChuncks.GetComponent <ChunkCore> ().SaveChunk();
             }
 
             SaveChunks.Clear();
         }catch{
         }
 
     }
 
 }
 

Comment
Add comment · Show 4
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Hez · Nov 12, 2015 at 11:52 PM 0
Share

you know, listing all the scrips needed for this to work makes me think maybe I should combined a few scrips.

avatar image ijidau Hez · Nov 12, 2015 at 11:58 PM 1
Share

Wowzers... I don't think anyone is going to take the time to read and figure that out. Can you do a screen grab and post a video of the result? This might help us understand what you are trying to achieve, from which we can hopefully point you in the right direction.

avatar image Hez ijidau · Nov 13, 2015 at 12:06 AM 0
Share

$$anonymous$$y computer can't run most screen capture, and the problem is fps going to 0, so yes a screen shot is what I am seeing, idk if it will help :( I am slowly starting to think it is a lack of good computer problem, also I save to a file directly under C:\, and I am stating to suspect that it may be a part of the problem, and I am trying to fix that now.

also, I A$$anonymous$$ saveing 20 5.3kb files, and I think that might also have an effect.

Show more comments

1 Reply

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by JoshuaMcKenzie · Nov 13, 2015 at 01:59 AM

Your code is not documented very well so its a little difficult to pick up, Looking at your CheckChunks() function it appears that when you call this function you're checking a 7x7 grid of chunks and you attempt on checking them 200 times a second. Do you really need to check that many chunks? How big are they? Do you really need to check them that frequently? can you get away with checking a 3x3 once every... say 5 seconds?

You're also attempting to save all Chunks every 2 seconds, thus you're constantly writing to the disc when most times you don't need to. A chunk should be saved when its unloaded and when its changed (you can wait up to 10-20 seconds after the most recent change before attempting a save, long enough to reduce overhead and short enough that won't frustrate the player due to a potential crash).

I see that you are using many Try/Catches in your code. While they can work, they can be noticeably slower, especially if iterated over several times a second. It is better if you can structure your code where a try catch is never needed

I see your writing to file as a Plaintext file which for heavy data objects can slow down Saving and loading, and if its constantly creating new files that has overhead to it as well. If you're going to be loading and saving a lot of data frequently I would recommend a different approach, I would recommend looking into Scriptible Objects and the Live Tutorial. Even though the tutorial itself admits to being boring in the first half, the information it provides is pretty relevant to your performance issues as Scriptible Objects are similar to Databases in the sense that they are able to load/save volumes of data pretty quickly.

I have only seen a tiny portion of your game so I can't fathom how much work you may actually need to fix your issues, it may even be the issues your having are also due to some other code you haven't shown.

Using the Profiler is the easiest way at finding where the performance bottlenecks are.

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
â–¼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Hez · Nov 13, 2015 at 03:14 AM 0
Share

I have failed to provide vital information in a drastic oversight, despite that you have helped me. ty

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

33 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Multiple Cars not working 1 Answer

Distribute terrain in zones 3 Answers

15625 blocks are making my game lag? Increase performance? 3 Answers

Making a bubble level (not a game but work tool) 1 Answer

Null Reference Exception For Blocks C# 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges