Browse Source

[Netrumble] Add Content.mgcb to build resources per platform.

Dominique Louis 3 weeks ago
parent
commit
dbcd3a16e7
78 changed files with 1479 additions and 671 deletions
  1. BIN
      NetRumble/Core/Content/Audio/wav/asteroid_touch.xnb
  2. BIN
      NetRumble/Core/Content/Audio/wav/explosion_large.xnb
  3. BIN
      NetRumble/Core/Content/Audio/wav/explosion_medium.xnb
  4. BIN
      NetRumble/Core/Content/Audio/wav/explosion_shockwave.xnb
  5. BIN
      NetRumble/Core/Content/Audio/wav/fire_laser1.xnb
  6. BIN
      NetRumble/Core/Content/Audio/wav/fire_laser2.xnb
  7. BIN
      NetRumble/Core/Content/Audio/wav/fire_laser3.xnb
  8. BIN
      NetRumble/Core/Content/Audio/wav/fire_rocket1.xnb
  9. BIN
      NetRumble/Core/Content/Audio/wav/fire_rocket2.xnb
  10. BIN
      NetRumble/Core/Content/Audio/wav/menu_scroll.xnb
  11. BIN
      NetRumble/Core/Content/Audio/wav/menu_select.xnb
  12. BIN
      NetRumble/Core/Content/Audio/wav/player_spawn.xnb
  13. BIN
      NetRumble/Core/Content/Audio/wav/powerup_spawn.xnb
  14. BIN
      NetRumble/Core/Content/Audio/wav/powerup_touch.xnb
  15. BIN
      NetRumble/Core/Content/Audio/wav/rocket.xnb
  16. 0 0
      NetRumble/Core/Content/Audio/wma/One Step Beyond.wma
  17. 307 0
      NetRumble/Core/Content/Content.mgcb
  18. 54 0
      NetRumble/Core/Content/Effects/BloomCombine.fx
  19. 29 0
      NetRumble/Core/Content/Effects/BloomExtract.fx
  20. 39 0
      NetRumble/Core/Content/Effects/Clouds.fx
  21. BIN
      NetRumble/Core/Content/Effects/Clouds.xnb
  22. 37 0
      NetRumble/Core/Content/Effects/GaussianBlur.fx
  23. 77 0
      NetRumble/Core/Content/Effects/Macros.hlsl
  24. BIN
      NetRumble/Core/Content/Fonts/MenuFont.xnb
  25. BIN
      NetRumble/Core/Content/Fonts/MessageBox.xnb
  26. BIN
      NetRumble/Core/Content/Fonts/NetRumbleFont.xnb
  27. BIN
      NetRumble/Core/Content/One Step Beyond.xnb
  28. BIN
      NetRumble/Core/Content/Textures/Particles/defaultParticle.xnb
  29. BIN
      NetRumble/Core/Content/Textures/Particles/particle.xnb
  30. BIN
      NetRumble/Core/Content/Textures/Particles/smoke.xnb
  31. BIN
      NetRumble/Core/Content/Textures/Particles/spark.xnb
  32. BIN
      NetRumble/Core/Content/Textures/asteroid0.xnb
  33. BIN
      NetRumble/Core/Content/Textures/asteroid1.xnb
  34. BIN
      NetRumble/Core/Content/Textures/asteroid2.xnb
  35. BIN
      NetRumble/Core/Content/Textures/barrierEnd.xnb
  36. BIN
      NetRumble/Core/Content/Textures/barrierPurple.xnb
  37. BIN
      NetRumble/Core/Content/Textures/barrierRed.xnb
  38. BIN
      NetRumble/Core/Content/Textures/blank.xnb
  39. BIN
      NetRumble/Core/Content/Textures/chatAble.xnb
  40. BIN
      NetRumble/Core/Content/Textures/chatMute.xnb
  41. BIN
      NetRumble/Core/Content/Textures/chatTalking.xnb
  42. BIN
      NetRumble/Core/Content/Textures/clouds.xnb
  43. BIN
      NetRumble/Core/Content/Textures/explosion.xnb
  44. BIN
      NetRumble/Core/Content/Textures/laser.xnb
  45. BIN
      NetRumble/Core/Content/Textures/mine.xnb
  46. BIN
      NetRumble/Core/Content/Textures/powerupDoubleLaser.xnb
  47. BIN
      NetRumble/Core/Content/Textures/powerupRocket.xnb
  48. BIN
      NetRumble/Core/Content/Textures/powerupTripleLaser.xnb
  49. BIN
      NetRumble/Core/Content/Textures/ready.xnb
  50. BIN
      NetRumble/Core/Content/Textures/rocket.xnb
  51. BIN
      NetRumble/Core/Content/Textures/ship0.xnb
  52. BIN
      NetRumble/Core/Content/Textures/ship0Overlay.xnb
  53. BIN
      NetRumble/Core/Content/Textures/ship1.xnb
  54. BIN
      NetRumble/Core/Content/Textures/ship1Overlay.xnb
  55. BIN
      NetRumble/Core/Content/Textures/ship2.xnb
  56. BIN
      NetRumble/Core/Content/Textures/ship2Overlay.xnb
  57. BIN
      NetRumble/Core/Content/Textures/ship3.xnb
  58. BIN
      NetRumble/Core/Content/Textures/ship3Overlay.xnb
  59. BIN
      NetRumble/Core/Content/Textures/shipShields.xnb
  60. BIN
      NetRumble/Core/Content/Textures/title.xnb
  61. 20 8
      NetRumble/Core/NetRumbleGame.cs
  62. 15 17
      NetRumble/Core/OperationCompletedEventArgs.cs
  63. 9 53
      NetRumble/Core/Rendering/Particles/ParticleEffect.cs
  64. 33 71
      NetRumble/Core/Rendering/Starfield.cs
  65. 15 73
      NetRumble/Core/Screens/GameplayScreen.cs
  66. 70 215
      NetRumble/Core/Screens/MainMenuScreen.cs
  67. 65 100
      NetRumble/Core/Screens/NetworkBusyScreen.cs
  68. 55 77
      NetRumble/Core/Screens/SearchResultsScreen.cs
  69. 27 0
      NetRumble/Core/UIUtility.cs
  70. 22 7
      NetRumble/Platforms/Android/MainActivity.cs
  71. 2 7
      NetRumble/Platforms/Android/NetRumble.Android.csproj
  72. BIN
      NetRumble/Platforms/Android/Resources/drawable/icon.png
  73. 491 0
      NetRumble/Platforms/Android/Resources/drawable/icon.svg
  74. 4 0
      NetRumble/Platforms/Android/Resources/values/strings.xml
  75. 1 6
      NetRumble/Platforms/Desktop/NetRumble.DesktopGL.csproj
  76. 1 6
      NetRumble/Platforms/Windows/NetRumble.Windows.csproj
  77. 1 6
      NetRumble/Platforms/iOS/NetRumble.iOS.csproj
  78. 105 25
      NetRumble/README.md

BIN
NetRumble/Core/Content/Audio/wav/asteroid_touch.xnb


BIN
NetRumble/Core/Content/Audio/wav/explosion_large.xnb


BIN
NetRumble/Core/Content/Audio/wav/explosion_medium.xnb


BIN
NetRumble/Core/Content/Audio/wav/explosion_shockwave.xnb


BIN
NetRumble/Core/Content/Audio/wav/fire_laser1.xnb


BIN
NetRumble/Core/Content/Audio/wav/fire_laser2.xnb


BIN
NetRumble/Core/Content/Audio/wav/fire_laser3.xnb


BIN
NetRumble/Core/Content/Audio/wav/fire_rocket1.xnb


BIN
NetRumble/Core/Content/Audio/wav/fire_rocket2.xnb


BIN
NetRumble/Core/Content/Audio/wav/menu_scroll.xnb


BIN
NetRumble/Core/Content/Audio/wav/menu_select.xnb


BIN
NetRumble/Core/Content/Audio/wav/player_spawn.xnb


BIN
NetRumble/Core/Content/Audio/wav/powerup_spawn.xnb


BIN
NetRumble/Core/Content/Audio/wav/powerup_touch.xnb


BIN
NetRumble/Core/Content/Audio/wav/rocket.xnb


+ 0 - 0
NetRumble/Core/Content/One Step Beyond.wma → NetRumble/Core/Content/Audio/wma/One Step Beyond.wma


+ 307 - 0
NetRumble/Core/Content/Content.mgcb

@@ -0,0 +1,307 @@
+
+#----------------------------- Global Properties ----------------------------#
+
+/outputDir:bin/$(Platform)
+/intermediateDir:obj/$(Platform)
+/platform:DesktopGL
+/config:$(Configuration)
+/profile:HiDef
+/compress:False
+
+#-------------------------------- References --------------------------------#
+
+
+
+#---------------------------------- Content ---------------------------------#
+
+#begin Effects/BloomCombine.fx
+/importer:EffectImporter
+/processor:EffectProcessor
+/build:Effects/BloomCombine.fx
+
+#begin Effects/BloomExtract.fx
+/importer:EffectImporter
+/processor:EffectProcessor
+/build:Effects/BloomExtract.fx
+
+#begin Effects/GaussianBlur.fx
+/importer:EffectImporter
+/processor:EffectProcessor
+/build:Effects/GaussianBlur.fx
+
+#begin Effects/Clouds.fx
+/importer:EffectImporter
+/processor:EffectProcessor
+/build:Effects/Clouds.fx
+
+#begin Textures/blank.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/blank.png
+
+#begin Textures/title.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/title.png
+
+#begin Fonts/MenuFont.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/build:Fonts/MenuFont.spritefont
+
+#begin Fonts/MessageBox.spritefont
+/importer:FontDescriptionImporter
+/processor:FontDescriptionProcessor
+/build:Fonts/MessageBox.spritefont
+
+#begin Fonts/NetRumbleFont.png
+/importer:TextureImporter
+/processor:FontTextureProcessor
+/build:Fonts/NetRumbleFont.png
+
+#begin Textures/asteroid0.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/asteroid0.png
+
+#begin Textures/asteroid1.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/asteroid1.png
+
+#begin Textures/asteroid2.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/asteroid2.png
+
+#begin Textures/barrierEnd.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/barrierEnd.png
+
+#begin Textures/barrierPurple.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/barrierPurple.png
+
+#begin Textures/barrierRed.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/barrierRed.png
+
+#begin Textures/mine.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/mine.png
+
+#begin Textures/powerupDoubleLaser.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/powerupDoubleLaser.png
+
+#begin Textures/powerupRocket.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/powerupRocket.png
+
+#begin Textures/powerupTripleLaser.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/powerupTripleLaser.png
+
+#begin Textures/ship0.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship0.png
+
+#begin Textures/ship0Overlay.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship0Overlay.png
+
+#begin Textures/ship1.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship1.png
+
+#begin Textures/ship1Overlay.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship1Overlay.png
+
+#begin Textures/ship2.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship2.png
+
+#begin Textures/ship2Overlay.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship2Overlay.png
+
+#begin Textures/ship3.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship3.png
+
+#begin Textures/ship3Overlay.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ship3Overlay.png
+
+#begin Textures/shipShields.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/shipShields.png
+
+#begin Textures/laser.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/laser.png
+
+#begin Textures/rocket.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/rocket.png
+
+#begin Textures/clouds.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:PremultiplyAlpha=False
+/build:Textures/clouds.png
+
+#begin Textures/chatAble.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/chatAble.png
+
+#begin Textures/chatMute.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/chatMute.png
+
+#begin Textures/chatTalking.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/chatTalking.png
+
+#begin Textures/ready.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/ready.png
+
+#begin Textures/explosion.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/explosion.png
+
+#begin Textures/Particles/smoke.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:PremultiplyAlpha=False
+/build:Textures/Particles/smoke.png
+
+#begin Textures/Particles/particle.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:PremultiplyAlpha=False
+/build:Textures/Particles/particle.png
+
+#begin Textures/Particles/spark.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/processorParam:PremultiplyAlpha=False
+/build:Textures/Particles/spark.png
+
+#begin Textures/Particles/defaultParticle.png
+/importer:TextureImporter
+/processor:TextureProcessor
+/build:Textures/Particles/defaultParticle.png
+
+
+/copy:Particles/laserExplosion.xml
+/copy:Particles/mineExplosion.xml
+/copy:Particles/rocketExplosion.xml
+/copy:Particles/rocketTrail.xml
+/copy:Particles/shipExplosion.xml
+/copy:Particles/shipSpawn.xml
+
+#begin Audio/wav/menu_scroll.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/menu_scroll.wav
+
+#begin Audio/wma/One Step Beyond.wma
+#/importer:WmaImporter
+#/processor:SongProcessor
+#/build:Audio/wma/One Step Beyond.wma
+
+#begin Audio/wav/powerup_spawn.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/powerup_spawn.wav
+
+#begin Audio/wav/powerup_touch.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/powerup_touch.wav
+
+#begin Audio/wav/explosion_large.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/explosion_large.wav
+
+#begin Audio/wav/rocket.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/rocket.wav
+
+#begin Audio/wav/explosion_medium.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/explosion_medium.wav
+
+#begin Audio/wav/fire_rocket1.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/fire_rocket1.wav
+
+#begin Audio/wav/fire_rocket2.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/fire_rocket2.wav
+
+#begin Audio/wav/fire_laser1.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/fire_laser1.wav
+
+#begin Audio/wav/fire_laser2.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/fire_laser2.wav
+
+#begin Audio/wav/fire_laser3.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/fire_laser3.wav
+
+#begin Audio/wav/asteroid_touch.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/asteroid_touch.wav
+
+#begin Audio/wav/player_spawn.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/player_spawn.wav
+
+#begin Audio/wav/explosion_shockwave.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/explosion_shockwave.wav
+
+#begin Audio/wav/menu_select.wav
+/importer:WavImporter
+/processor:SoundEffectProcessor
+/build:Audio/wav/menu_select.wav

+ 54 - 0
NetRumble/Core/Content/Effects/BloomCombine.fx

@@ -0,0 +1,54 @@
+// Pixel shader combines the bloom image with the original
+// scene, using tweakable intensity levels and saturation.
+// This is the final step in applying a bloom postprocess.
+
+#include "Macros.hlsl"
+
+BEGIN_CONSTANTS
+float BloomIntensity;
+float BaseIntensity;
+float BloomSaturation;
+float BaseSaturation;
+END_CONSTANTS
+
+DECLARE_TEXTURE(BloomSampler, 0);
+DECLARE_TEXTURE(BaseSampler, 1);
+
+
+// Helper for modifying the saturation of a color.
+float4 AdjustSaturation(float4 color, float saturation)
+{
+    // The constants 0.3, 0.59, and 0.11 are chosen because the
+    // human eye is more sensitive to green light, and less to blue.
+    float grey = dot(color, float3(0.3, 0.59, 0.11));
+
+    return lerp(grey, color, saturation);
+}
+
+
+float4 PixelShaderF(float2 texCoord : TEXCOORD0) : COLOR0
+{
+    // Look up the bloom and original base image colors.
+    float4 bloom = SAMPLE_TEXTURE(BloomSampler, texCoord);
+    float4 base = SAMPLE_TEXTURE(BaseSampler, texCoord);
+    
+    // Adjust color saturation and intensity.
+    bloom = AdjustSaturation(bloom, BloomSaturation) * BloomIntensity;
+    base = AdjustSaturation(base, BaseSaturation) * BaseIntensity;
+    
+    // Darken down the base image in areas where there is a lot of bloom,
+    // to prevent things looking excessively burned-out.
+    base *= (1 - saturate(bloom));
+    
+    // Combine the two images.
+    return base + bloom;
+}
+
+
+technique BloomCombine
+{
+    pass Pass1
+    {
+    PixelShader = compile ps_4_0_level_9_1 PixelShaderF();
+    }
+}

+ 29 - 0
NetRumble/Core/Content/Effects/BloomExtract.fx

@@ -0,0 +1,29 @@
+// Pixel shader extracts the brighter areas of an image.
+// This is the first step in applying a bloom postprocess.
+
+#include "Macros.hlsl"
+
+DECLARE_TEXTURE(TextureSampler, 0);
+
+BEGIN_CONSTANTS
+float BloomThreshold;
+END_CONSTANTS
+
+
+float4 PixelShaderF(float2 texCoord : TEXCOORD0) : COLOR0
+{
+    // Look up the original image color.
+    float4 c = SAMPLE_TEXTURE(TextureSampler, texCoord);
+
+    // Adjust it to keep only values brighter than the specified threshold.
+    return saturate((c - BloomThreshold) / (1 - BloomThreshold));
+}
+
+
+technique BloomExtract
+{
+    pass Pass1
+    {
+    PixelShader = compile ps_4_0_level_9_1 PixelShaderF();
+    }
+}

+ 39 - 0
NetRumble/Core/Content/Effects/Clouds.fx

@@ -0,0 +1,39 @@
+//-----------------------------------------------------------------------------
+// Clouds.fx
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+#include "Macros.hlsl"
+
+// Constants
+BEGIN_CONSTANTS
+float2 Position;
+END_CONSTANTS
+
+// Texture + sampler (portable across SM3/SM4 via macros)
+DECLARE_TEXTURE(CloudTexture, 0);
+
+float4 MainPS(float2 texCoord : TEXCOORD0) : COLOR0
+{
+    // Blue component
+    float2 coord1 = texCoord + Position * 0.00025f;
+    coord1 *= 0.5f;
+    float4 results = float4(0,0,1,0.25) * SAMPLE_TEXTURE(CloudTexture, coord1);
+
+    // Green component
+    float2 coord2 = texCoord + Position * 0.00025f + float2(0.25f, -0.15f);
+    coord2 *= 0.4f;
+    results += float4(0,1,0,0.15) * SAMPLE_TEXTURE(CloudTexture, coord2);
+
+    return results;
+}
+
+technique Clouds
+{
+    pass P0
+    {
+    PixelShader = compile ps_4_0_level_9_1 MainPS();
+    }
+}

BIN
NetRumble/Core/Content/Effects/Clouds.xnb


+ 37 - 0
NetRumble/Core/Content/Effects/GaussianBlur.fx

@@ -0,0 +1,37 @@
+// Pixel shader applies a one dimensional gaussian blur filter.
+// This is used twice by the bloom postprocess, first to
+// blur horizontally, and then again to blur vertically.
+
+#include "Macros.hlsl"
+
+DECLARE_TEXTURE(TextureSampler, 0);
+
+#define SAMPLE_COUNT 15
+
+BEGIN_CONSTANTS
+float2 SampleOffsets[SAMPLE_COUNT];
+float SampleWeights[SAMPLE_COUNT];
+END_CONSTANTS
+
+
+float4 PixelShaderF(float2 texCoord : TEXCOORD0) : COLOR0
+{
+    float4 c = 0;
+    
+    // Combine a number of weighted image filter taps.
+    for (int i = 0; i < SAMPLE_COUNT; i++)
+    {
+    c += SAMPLE_TEXTURE(TextureSampler, texCoord + SampleOffsets[i]) * SampleWeights[i];
+    }
+    
+    return c;
+}
+
+
+technique GaussianBlur
+{
+    pass Pass1
+    {
+    PixelShader = compile ps_4_0_level_9_1 PixelShaderF();
+    }
+}

+ 77 - 0
NetRumble/Core/Content/Effects/Macros.hlsl

@@ -0,0 +1,77 @@
+// MonoGame - Copyright (C) The MonoGame Team
+// This file is subject to the terms and conditions defined in
+// file 'LICENSE.txt', which is part of this source code package.
+
+#if !defined(MACROS_H)
+#define MACROS_H
+
+
+#if defined(SM4)
+
+// Macros for targetting shader model 4.0 (DX11)
+#define VS_SHADERMODEL vs_4_0
+#define PS_SHADERMODEL ps_4_0
+
+#define TECHNIQUE(name, vsname, psname ) \
+	technique name { pass { VertexShader = compile vs_4_0_level_9_1 vsname (); PixelShader = compile ps_4_0_level_9_1 psname(); } }
+
+#define BEGIN_CONSTANTS     cbuffer Parameters : register(b0) {
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS       };
+
+#define _vs(r)
+#define _ps(r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE_FORMAT(Name, Format, Index) \
+    Texture2D<Format> Name : register(t##Index); \
+    sampler Name##Sampler : register(s##Index)
+
+#define DECLARE_TEXTURE(Name, Index) \
+    Texture2D<float4> Name : register(t##Index); \
+    sampler Name##Sampler : register(s##Index)
+
+#define DECLARE_CUBEMAP(Name, Index) \
+    TextureCube<float4> Name : register(t##Index); \
+    sampler Name##Sampler : register(s##Index)
+
+#define LOAD_TEXTURE(Name, texCoord)  Name.Load(texCoord)
+#define SAMPLE_TEXTURE(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord)  Name.Sample(Name##Sampler, texCoord)
+
+#define UNROLL [unroll]
+
+#else // !defined(SM4)
+
+#define SV_POSITION POSITION
+#define SV_TARGET0 COLOR
+#define VS_SHADERMODEL vs_3_0
+#define PS_SHADERMODEL ps_3_0
+
+#define BEGIN_CONSTANTS
+#define MATRIX_CONSTANTS
+#define END_CONSTANTS
+
+#define _vs(r)  : register(vs, r)
+#define _ps(r)  : register(ps, r)
+#define _cb(r)
+
+#define DECLARE_TEXTURE(Name, Index) \
+    sampler2D Name : register(s##Index)
+
+#define DECLARE_CUBEMAP(Name, Index) \
+    samplerCUBE Name : register(s##Index)
+
+#define DECLARE_TEXTURE_FORMAT(Name, Format, Index) \
+    sampler2D Name : register(s##Index)
+
+#define SAMPLE_TEXTURE(Name, texCoord)  tex2D(Name, texCoord)
+#define SAMPLE_CUBEMAP(Name, texCoord)  texCUBE(Name, texCoord)
+#define LOAD_TEXTURE(Name, texCoord)  tex2D(Name, texCoord.xy)
+
+#define UNROLL [unroll]
+
+#endif
+
+
+#endif // MACROS_H

BIN
NetRumble/Core/Content/Fonts/MenuFont.xnb


BIN
NetRumble/Core/Content/Fonts/MessageBox.xnb


BIN
NetRumble/Core/Content/Fonts/NetRumbleFont.xnb


BIN
NetRumble/Core/Content/One Step Beyond.xnb


BIN
NetRumble/Core/Content/Textures/Particles/defaultParticle.xnb


BIN
NetRumble/Core/Content/Textures/Particles/particle.xnb


BIN
NetRumble/Core/Content/Textures/Particles/smoke.xnb


BIN
NetRumble/Core/Content/Textures/Particles/spark.xnb


BIN
NetRumble/Core/Content/Textures/asteroid0.xnb


BIN
NetRumble/Core/Content/Textures/asteroid1.xnb


BIN
NetRumble/Core/Content/Textures/asteroid2.xnb


BIN
NetRumble/Core/Content/Textures/barrierEnd.xnb


BIN
NetRumble/Core/Content/Textures/barrierPurple.xnb


BIN
NetRumble/Core/Content/Textures/barrierRed.xnb


BIN
NetRumble/Core/Content/Textures/blank.xnb


BIN
NetRumble/Core/Content/Textures/chatAble.xnb


BIN
NetRumble/Core/Content/Textures/chatMute.xnb


BIN
NetRumble/Core/Content/Textures/chatTalking.xnb


BIN
NetRumble/Core/Content/Textures/clouds.xnb


BIN
NetRumble/Core/Content/Textures/explosion.xnb


BIN
NetRumble/Core/Content/Textures/laser.xnb


BIN
NetRumble/Core/Content/Textures/mine.xnb


BIN
NetRumble/Core/Content/Textures/powerupDoubleLaser.xnb


BIN
NetRumble/Core/Content/Textures/powerupRocket.xnb


BIN
NetRumble/Core/Content/Textures/powerupTripleLaser.xnb


BIN
NetRumble/Core/Content/Textures/ready.xnb


BIN
NetRumble/Core/Content/Textures/rocket.xnb


BIN
NetRumble/Core/Content/Textures/ship0.xnb


BIN
NetRumble/Core/Content/Textures/ship0Overlay.xnb


BIN
NetRumble/Core/Content/Textures/ship1.xnb


BIN
NetRumble/Core/Content/Textures/ship1Overlay.xnb


BIN
NetRumble/Core/Content/Textures/ship2.xnb


BIN
NetRumble/Core/Content/Textures/ship2Overlay.xnb


BIN
NetRumble/Core/Content/Textures/ship3.xnb


BIN
NetRumble/Core/Content/Textures/ship3Overlay.xnb


BIN
NetRumble/Core/Content/Textures/shipShields.xnb


BIN
NetRumble/Core/Content/Textures/title.xnb


+ 20 - 8
NetRumble/Core/NetRumbleGame.cs

@@ -38,7 +38,7 @@ namespace NetRumble
         /// <summary>
         /// The graphics device manager used to render the game.
         /// </summary>
-        GraphicsDeviceManager graphics;
+        GraphicsDeviceManager graphicsDeviceManager;
 
 
 
@@ -61,13 +61,25 @@ namespace NetRumble
         public NetRumbleGame()
         {
             // initialize the graphics device manager
-            graphics = new GraphicsDeviceManager(this);
+            graphicsDeviceManager = new GraphicsDeviceManager(this);
 
-            graphics.PreferredBackBufferWidth = 1280;
-            graphics.PreferredBackBufferHeight = 720;
-#if MOBILE
-            graphics.IsFullScreen = true;
-#endif
+            graphicsDeviceManager.PreferredBackBufferWidth = 1280;
+            graphicsDeviceManager.PreferredBackBufferHeight = 720;
+
+            if (UIUtility.IsMobile)
+            {
+                graphicsDeviceManager.IsFullScreen = true;
+                IsMouseVisible = false;
+            }
+            else if (UIUtility.IsDesktop)
+            {
+                graphicsDeviceManager.IsFullScreen = false;
+                IsMouseVisible = true;
+            }
+            else
+            {
+                throw new PlatformNotSupportedException();
+            }
 
             // initialize the content manager
             Content.RootDirectory = "Content";
@@ -171,7 +183,7 @@ namespace NetRumble
         /// <param name="gameTime">Provides a snapshot of timing values.</param>
         protected override void Draw(GameTime gameTime)
         {
-            graphics.GraphicsDevice.Clear(Color.MonoGameOrange);
+            graphicsDeviceManager.GraphicsDevice.Clear(Color.MonoGameOrange);
 
             base.Draw(gameTime);
         }

+ 15 - 17
NetRumble/Core/OperationCompletedEventArgs.cs

@@ -12,35 +12,33 @@ namespace NetRumble
     /// <summary>
     /// Custom EventArgs class used by the NetworkBusyScreen.OperationCompleted event.
     /// </summary>
-    /// <remarks>Based on a class in the Network Game State Management sample.</remarks>
     class OperationCompletedEventArgs : EventArgs
     {
+        /// <summary>
+        /// Gets or sets the result of the network operation that has just completed.
+        /// </summary>
+        public object Result { get; set; }
 
+        /// <summary>
+        /// Gets or sets the exception that caused the operation to fail, if any.
+        /// </summary>
+        public Exception Exception { get; set; }
 
         /// <summary>
-        /// Gets or sets the IAsyncResult associated with
-        /// the network operation that has just completed.
+        /// Constructs a new event arguments class.
         /// </summary>
-        public IAsyncResult AsyncResult
+        public OperationCompletedEventArgs(object result)
         {
-            get { return asyncResult; }
-            set { asyncResult = value; }
+            this.Result = result;
         }
 
-        IAsyncResult asyncResult;
-
-
-
-
-
         /// <summary>
-        /// Constructs a new event arguments class.
+        /// Constructs a new event arguments class with an optional exception.
         /// </summary>
-        public OperationCompletedEventArgs(IAsyncResult asyncResult)
+        public OperationCompletedEventArgs(object result, Exception exception)
         {
-            this.asyncResult = asyncResult;
+            this.Result = result;
+            this.Exception = exception;
         }
-
-
     }
 }

+ 9 - 53
NetRumble/Core/Rendering/Particles/ParticleEffect.cs

@@ -21,8 +21,6 @@ namespace NetRumble
     /// </summary>
     public class ParticleEffect
     {
-
-
         /// <summary>
         /// The name of the particle effect.
         /// </summary>
@@ -31,20 +29,13 @@ namespace NetRumble
         /// <summary>
         /// The particle systems in this effect.
         /// </summary>
-        private Collection<ParticleSystem> particleSystems =
-            new Collection<ParticleSystem>();
-
-
-
-
-
+        private Collection<ParticleSystem> particleSystems = new Collection<ParticleSystem>();
 
         /// <summary>
         /// The position of the particle effect in the world.
         /// </summary>
         private Vector2 position;
 
-
         /// <summary>
         /// The gameplay object that the system is following, if any.
         /// </summary>
@@ -56,11 +47,6 @@ namespace NetRumble
             set { followObject = value; }
         }
 
-
-
-
-
-
         /// <summary>
         /// If true, the particle effect is currently active.
         /// </summary>
@@ -71,17 +57,11 @@ namespace NetRumble
             get { return active; }
         }
 
-
-
-
-
-
         /// <summary>
         /// Create a new particle effect.
         /// </summary>
         public ParticleEffect() { }
 
-
         /// <summary>
         /// Create a new particle effect that is a clone of another one.
         /// </summary>
@@ -103,7 +83,6 @@ namespace NetRumble
             return clone;
         }
 
-        
         /// <summary>
         /// Initialize the particle effect.
         /// </summary>
@@ -120,7 +99,6 @@ namespace NetRumble
             active = true;
         }
 
-
         /// <summary>
         /// Reset the particle effect.
         /// </summary>
@@ -136,11 +114,6 @@ namespace NetRumble
             active = true;
         }
 
-
-
-
-
-
         /// <summary>
         /// Update the particle effect.
         /// </summary>
@@ -173,11 +146,6 @@ namespace NetRumble
             }
         }
 
-
-
-
-
-
         /// <summary>
         /// Draw the particle effect.
         /// </summary>
@@ -198,11 +166,6 @@ namespace NetRumble
             }
         }
 
-
-
-
-
-
         /// <summary>
         /// Stop the particle effect.
         /// </summary>
@@ -225,11 +188,6 @@ namespace NetRumble
             }
         }
 
-
-
-
-
-
         public string Name
         {
             get { return name; }
@@ -254,11 +212,6 @@ namespace NetRumble
             get { return particleSystems as Collection<ParticleSystem>; }
         }
 
-
-
-
-
-
         /// <summary>
         /// Create a new ParticleEffect object from the data in an XML file.
         /// </summary>
@@ -266,10 +219,13 @@ namespace NetRumble
         /// <returns>A new ParticleEffect object.</returns>
         public static ParticleEffect Load(string filepath)
         {
-            XmlSerializer serializer = new XmlSerializer(typeof(ParticleEffect));
-            return (ParticleEffect)serializer.Deserialize(File.OpenRead(filepath));
+            //XmlSerializer serializer = new XmlSerializer(typeof(ParticleEffect));
+            //return (ParticleEffect)serializer.Deserialize(File.OpenRead(filepath));
+            var serializer = new XmlSerializer(typeof(ParticleEffect));
+            using (var stream = TitleContainer.OpenStream(filepath))
+            {
+                return (ParticleEffect)serializer.Deserialize(stream);
+            }
         }
-
-
     }
-}
+}

+ 33 - 71
NetRumble/Core/Rendering/Starfield.cs

@@ -17,8 +17,6 @@ namespace NetRumble
     /// </summary>
     public class Starfield : IDisposable
     {
-
-
         /// <summary>
         /// The number of stars in the starfield.
         /// </summary>
@@ -32,17 +30,17 @@ namespace NetRumble
         /// <summary>
         /// The colors for each layer of stars.
         /// </summary>
-        static readonly Color[] layerColors = new Color[numberOfLayers] 
-            { 
-                new Color(255, 255, 255, 255), 
-                new Color(255, 255, 255, 216), 
-                new Color(255, 255, 255, 192), 
-                new Color(255, 255, 255, 160), 
-                new Color(255, 255, 255, 128), 
-                new Color(255, 255, 255, 96), 
-                new Color(255, 255, 255, 64), 
-                new Color(255, 255, 255, 32) 
-            };
+        static readonly Color[] layerColors = new Color[numberOfLayers]
+        {
+            new Color(255, 255, 255, 255),
+            new Color(255, 255, 255, 216),
+            new Color(255, 255, 255, 192),
+            new Color(255, 255, 255, 160),
+            new Color(255, 255, 255, 128),
+            new Color(255, 255, 255, 96),
+            new Color(255, 255, 255, 64),
+            new Color(255, 255, 255, 32)
+        };
 
         /// <summary>
         /// The movement factor for each layer of stars, used in the parallax effect.
@@ -71,11 +69,6 @@ namespace NetRumble
         /// </summary>
         const int starSize = 2;
 
-
-
-
-
-
         /// <summary>
         /// The last position, used for the parallax effect.
         /// </summary>
@@ -91,11 +84,6 @@ namespace NetRumble
         /// </summary>
         private Vector2[] stars;
 
-
-
-
-
-
         /// <summary>
         /// The graphics device used to render the starfield.
         /// </summary>
@@ -124,17 +112,12 @@ namespace NetRumble
         /// <summary>
         /// The effect used to draw the clouds.
         /// </summary>
-        private Effect cloudEffect;
+        // TODO private Effect cloudEffect;
 
         /// <summary>
         /// The parameter on the cloud effect that receives the current position
         /// </summary>
-        private EffectParameter cloudEffectPosition;
-
-
-
-
-
+        // TODO private EffectParameter cloudEffectPosition;
 
         /// <summary>
         /// Create a new Starfield object.
@@ -142,7 +125,7 @@ namespace NetRumble
         /// <param name="position"></param>
         /// <param name="graphicsDevice">The graphics device used to render.</param>
         /// <param name="contentManager">The content manager for this object.</param>
-        public Starfield(Vector2 position, GraphicsDevice graphicsDevice, 
+        public Starfield(Vector2 position, GraphicsDevice graphicsDevice,
             ContentManager contentManager)
         {
             // safety-check the parameters, as they must be valid
@@ -164,7 +147,6 @@ namespace NetRumble
             Reset(position);
         }
 
-
         /// <summary>
         /// Load graphics data from the system.
         /// </summary>
@@ -174,10 +156,10 @@ namespace NetRumble
             cloudTexture = contentManager.Load<Texture2D>("Textures/clouds");
 
             // load the cloud effect
-            cloudEffect = contentManager.Load<Effect>("Effects/Clouds");
-			//cloudEffect = new CloudsEffect(graphicsDevice);
-            cloudEffectPosition = cloudEffect.Parameters["Position"];
-      
+            // TODO cloudEffect = contentManager.Load<Effect>("Effects/Clouds");
+            //cloudEffect = new CloudsEffect(graphicsDevice);
+            // TODO cloudEffectPosition = cloudEffect.Parameters["Position"];
+
             // create the star texture
             starTexture = new Texture2D(graphicsDevice, 1, 1, false, SurfaceFormat.Color);
             starTexture.SetData<Color>(new Color[] { Color.White });
@@ -186,15 +168,14 @@ namespace NetRumble
             spriteBatch = new SpriteBatch(graphicsDevice);
         }
 
-
         /// <summary>
         /// Release graphics data.
         /// </summary>
         public void UnloadContent()
         {
             cloudTexture = null;
-            cloudEffect = null;
-            cloudEffectPosition = null;
+            // TODO cloudEffect = null;
+            // TODO cloudEffectPosition = null;
 
             if (starTexture != null)
             {
@@ -209,7 +190,6 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Reset the stars and the parallax effect.
         /// </summary>
@@ -221,7 +201,7 @@ namespace NetRumble
             int viewportHeight = graphicsDevice.Viewport.Height;
             for (int i = 0; i < stars.Length; ++i)
             {
-                stars[i] = new Vector2(RandomMath.Random.Next(0, viewportWidth), 
+                stars[i] = new Vector2(RandomMath.Random.Next(0, viewportWidth),
                     RandomMath.Random.Next(0, viewportHeight));
             }
 
@@ -229,11 +209,6 @@ namespace NetRumble
             this.lastPosition = this.position = position;
         }
 
-
-
-
-
-
         /// <summary>
         /// Update and draw the starfield.
         /// </summary>
@@ -254,8 +229,7 @@ namespace NetRumble
             Vector2 movement = -1.0f * (position - lastPosition);
 
             // create a rectangle representing the screen dimensions of the starfield
-            Rectangle starfieldRectangle = new Rectangle(0, 0, 
-                graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height);
+            Rectangle starfieldRectangle = new Rectangle(0, 0, graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height);
 
             // draw a background color for the starfield
             spriteBatch.Begin();
@@ -263,12 +237,10 @@ namespace NetRumble
             spriteBatch.End();
 
             // draw the cloud texture
-            cloudEffectPosition.SetValue(this.position);
-            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, 
-                null, null, null, cloudEffect);
-            spriteBatch.Draw(cloudTexture, starfieldRectangle, null, Color.White, 0.0f,
-                Vector2.Zero, SpriteEffects.None, 1.0f);
-            spriteBatch.End();
+            /* TODO cloudEffectPosition.SetValue(this.position);
+            spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, cloudEffect);
+            spriteBatch.Draw(cloudTexture, starfieldRectangle, null, Color.White, 0.0f, Vector2.Zero, SpriteEffects.None, 1.0f);
+            spriteBatch.End();*/
 
             // if we've moved too far, then reset, as the stars will be moving too fast
             if (movement.Length() > maximumMovementPerUpdate)
@@ -289,42 +261,37 @@ namespace NetRumble
                 if (stars[i].X < starfieldRectangle.X)
                 {
                     stars[i].X = starfieldRectangle.X + starfieldRectangle.Width;
-                    stars[i].Y = starfieldRectangle.Y + 
+                    stars[i].Y = starfieldRectangle.Y +
                         RandomMath.Random.Next(starfieldRectangle.Height);
                 }
                 if (stars[i].X > (starfieldRectangle.X + starfieldRectangle.Width))
                 {
                     stars[i].X = starfieldRectangle.X;
-                    stars[i].Y = starfieldRectangle.Y + 
+                    stars[i].Y = starfieldRectangle.Y +
                         RandomMath.Random.Next(starfieldRectangle.Height);
                 }
                 if (stars[i].Y < starfieldRectangle.Y)
                 {
-                    stars[i].X = starfieldRectangle.X + 
+                    stars[i].X = starfieldRectangle.X +
                         RandomMath.Random.Next(starfieldRectangle.Width);
                     stars[i].Y = starfieldRectangle.Y + starfieldRectangle.Height;
                 }
-                if (stars[i].Y > 
+                if (stars[i].Y >
                     (starfieldRectangle.Y + graphicsDevice.Viewport.Height))
                 {
-                    stars[i].X = starfieldRectangle.X + 
+                    stars[i].X = starfieldRectangle.X +
                         RandomMath.Random.Next(starfieldRectangle.Width);
                     stars[i].Y = starfieldRectangle.Y;
                 }
 
                 // draw the star
-                spriteBatch.Draw(starTexture, 
+                spriteBatch.Draw(starTexture,
                     new Rectangle((int)stars[i].X, (int)stars[i].Y, starSize, starSize),
                     null, layerColors[depth]);
             }
             spriteBatch.End();
         }
 
-
-
-    
-
-
         /// <summary>
         /// Finalizes the Starfield object, calls Dispose(false)
         /// </summary>
@@ -333,7 +300,6 @@ namespace NetRumble
             Dispose(false);
         }
 
-
         /// <summary>
         /// Disposes the Starfield object.
         /// </summary>
@@ -343,7 +309,6 @@ namespace NetRumble
             GC.SuppressFinalize(this);
         }
 
-        
         /// <summary>
         /// Disposes this object.
         /// </summary>
@@ -369,8 +334,5 @@ namespace NetRumble
                 }
             }
         }
-
-
     }
-}
-
+}

+ 15 - 73
NetRumble/Core/Screens/GameplayScreen.cs

@@ -26,7 +26,6 @@ namespace NetRumble
     /// </remarks>
     public class GameplayScreen : GameScreen, IDisposable
     {
-
         /// <summary>
         /// The primary gameplay object.
         /// </summary>
@@ -47,11 +46,6 @@ namespace NetRumble
         /// </summary>
         private Vector2 winnerStringPosition;
 
-
-
-
-
-
         /// <summary>
         /// The bloom component, applied to part of the game world.
         /// </summary>
@@ -62,11 +56,6 @@ namespace NetRumble
         /// </summary>
         private Starfield starfield;
 
-
-
-
-
-
         /// <summary>
         /// The network session used in this game.
         /// </summary>
@@ -87,11 +76,6 @@ namespace NetRumble
         /// </summary>
         EventHandler<GamerLeftEventArgs> gamerLeftHandler;
 
-
-
-
-
-
         /// <summary>
         /// Construct a new GameplayScreen object.
         /// </summary>
@@ -123,7 +107,7 @@ namespace NetRumble
             gamerLeftHandler = new EventHandler<GamerLeftEventArgs>(
                 networkSession_GamerLeft);
             networkSession.GamerLeft += gamerLeftHandler;
-                
+
 
             // cache the local player's ship object
             if (networkSession.LocalGamers.Count > 0)
@@ -140,31 +124,20 @@ namespace NetRumble
             TransitionOffTime = TimeSpan.FromSeconds(1.0);
         }
 
-
         /// <summary>
         /// Load graphics content for the game.
         /// </summary>
         public override void LoadContent()
         {
-			
-			
-		// Developers Comment or uncomment the bloomComponent to run with effects or not
-		// ***************************	
-		// Comment or uncomment from here
-		// ***************************	
             // create and add the bloom effect
-#if !BLOOM
             bloomComponent = new BloomComponent(ScreenManager.Game);
             bloomComponent.Settings = BloomSettings.PresetSettings[0];
             ScreenManager.Game.Components.Add(bloomComponent);
             bloomComponent.Initialize();
             bloomComponent.Visible = false; // we want to control when bloom component is drawn
-#endif
-		// ***************************
-		// Comment or uncomment to here
-		// ***************************	
+
             // create the starfield
-            starfield = new Starfield(Vector2.Zero, ScreenManager.GraphicsDevice, 
+            starfield = new Starfield(Vector2.Zero, ScreenManager.GraphicsDevice,
                 ScreenManager.Content);
             starfield.LoadContent();
 
@@ -174,7 +147,6 @@ namespace NetRumble
             base.LoadContent();
         }
 
-
         /// <summary>
         /// Release graphics data.
         /// </summary>
@@ -188,11 +160,6 @@ namespace NetRumble
             base.UnloadContent();
         }
 
-
-
-
-
-
         /// <summary>
         /// Updates the state of the game. This method checks the GameScreen.IsActive
         /// property, so the game will stop updating when the pause menu is active,
@@ -241,15 +208,15 @@ namespace NetRumble
                     {
                         winnerString =
                             networkSession.AllGamers[world.WinnerIndex].Gamertag;
-                        winnerString += 
+                        winnerString +=
                             " has won the game!\nPress A to return to the lobby.";
-                        Vector2 winnerStringSize = 
+                        Vector2 winnerStringSize =
                             world.PlayerFont.MeasureString(winnerString);
                         winnerStringPosition = new Vector2(
-                            ScreenManager.GraphicsDevice.Viewport.X + 
-                                ScreenManager.GraphicsDevice.Viewport.Width / 2 - 
+                            ScreenManager.GraphicsDevice.Viewport.X +
+                                ScreenManager.GraphicsDevice.Viewport.Width / 2 -
                                 (float)Math.Floor(winnerStringSize.X / 2),
-                            ScreenManager.GraphicsDevice.Viewport.Y + 
+                            ScreenManager.GraphicsDevice.Viewport.Y +
                                 ScreenManager.GraphicsDevice.Viewport.Height / 2 -
                                 (float)Math.Floor(winnerStringSize.Y / 2));
                     }
@@ -275,7 +242,7 @@ namespace NetRumble
                     {
                         // If they pressed pause, bring up the pause menu screen.
                         const string message = "Exit the game?";
-                        MessageBoxScreen messageBox = new MessageBoxScreen(message, 
+                        MessageBoxScreen messageBox = new MessageBoxScreen(message,
                             false);
                         messageBox.Accepted += ExitMessageBoxAccepted;
                         ScreenManager.AddScreen(messageBox);
@@ -294,7 +261,6 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Event handler for when the user selects "yes" on the "are you sure
         /// you want to exit" message box.
@@ -305,7 +271,6 @@ namespace NetRumble
             world = null;
         }
 
-
         /// <summary>
         /// Force the end of a network session so that a new one can be joined.
         /// </summary>
@@ -318,7 +283,6 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Exit this screen.
         /// </summary>
@@ -341,7 +305,6 @@ namespace NetRumble
             base.ExitScreen();
         }
 
-
         /// <summary>
         /// Screen-specific update to gamer rich presence.
         /// </summary>
@@ -375,10 +338,6 @@ namespace NetRumble
             }
         }
 
-
-
-
-
         /// <summary>
         /// Draws the gameplay screen.
         /// </summary>
@@ -431,7 +390,6 @@ namespace NetRumble
                 ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
         }
 
-
         /// <summary>
         /// Draw the user interface elements of the game (scores, etc.).
         /// </summary>
@@ -443,11 +401,11 @@ namespace NetRumble
                 ScreenManager.SpriteBatch.Begin();
                 // draw players 0 - 3 at the top of the screen
                 Vector2 position = new Vector2(
-                    ScreenManager.GraphicsDevice.Viewport.Width * 0.2f, 
+                    ScreenManager.GraphicsDevice.Viewport.Width * 0.2f,
                     ScreenManager.GraphicsDevice.Viewport.Height * 0.1f);
                 for (int i = 0; i < Math.Min(4, networkSession.AllGamers.Count); i++)
                 {
-                    world.DrawPlayerData(totalTime, networkSession.AllGamers[i], 
+                    world.DrawPlayerData(totalTime, networkSession.AllGamers[i],
                         position, ScreenManager.SpriteBatch, false);
                     position.X += ScreenManager.GraphicsDevice.Viewport.Width * 0.2f;
                 }
@@ -463,7 +421,7 @@ namespace NetRumble
                 }
                 // draw players 8 - 11 at the left of the screen
                 position = new Vector2(
-                    ScreenManager.GraphicsDevice.Viewport.Width * 0.13f, 
+                    ScreenManager.GraphicsDevice.Viewport.Width * 0.13f,
                     ScreenManager.GraphicsDevice.Viewport.Height * 0.2f);
                 for (int i = 8; i < Math.Min(12, networkSession.AllGamers.Count); i++)
                 {
@@ -473,7 +431,7 @@ namespace NetRumble
                 }
                 // draw players 12 - 15 at the right of the screen
                 position = new Vector2(
-                    ScreenManager.GraphicsDevice.Viewport.Width * 0.9f, 
+                    ScreenManager.GraphicsDevice.Viewport.Width * 0.9f,
                     ScreenManager.GraphicsDevice.Viewport.Height * 0.2f);
                 for (int i = 12; i < Math.Min(16, networkSession.AllGamers.Count); i++)
                 {
@@ -485,18 +443,13 @@ namespace NetRumble
                 if (world.GameWon && !String.IsNullOrEmpty(winnerString))
                 {
                     ScreenManager.SpriteBatch.DrawString(world.PlayerFont, winnerString,
-                        winnerStringPosition, Color.White, 0f, Vector2.Zero, 1.3f, 
+                        winnerStringPosition, Color.White, 0f, Vector2.Zero, 1.3f,
                         SpriteEffects.None, 0f);
                 }
                 ScreenManager.SpriteBatch.End();
             }
         }
 
-
-
-
-
-
         /// <summary>
         /// Handle the end of the game session.
         /// </summary>
@@ -514,7 +467,6 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Handle the end of the session.
         /// </summary>
@@ -532,7 +484,6 @@ namespace NetRumble
             networkSession = null;
         }
 
-
         /// <summary>
         /// Handle a player leaving the game.
         /// </summary>
@@ -545,11 +496,6 @@ namespace NetRumble
             }
         }
 
-
-
-
-
-
         /// <summary>
         /// Finalizes the GameplayScreen object, calls Dispose(false)
         /// </summary>
@@ -558,7 +504,6 @@ namespace NetRumble
             Dispose(false);
         }
 
-
         /// <summary>
         /// Disposes the GameplayScreen object.
         /// </summary>
@@ -568,7 +513,6 @@ namespace NetRumble
             GC.SuppressFinalize(this);
         }
 
-        
         /// <summary>
         /// Disposes this object.
         /// </summary>
@@ -594,7 +538,5 @@ namespace NetRumble
                 }
             }
         }
-
-
     }
-}
+}

+ 70 - 215
NetRumble/Core/Screens/MainMenuScreen.cs

@@ -6,6 +6,7 @@
 //-----------------------------------------------------------------------------
 
 using System;
+using System.Collections.Generic;
 using Microsoft.Xna.Framework;
 using Microsoft.Xna.Framework.GamerServices;
 using Microsoft.Xna.Framework.Net;
@@ -80,8 +81,8 @@ namespace NetRumble
                         case MainMenuState.SignedInLocal:
                             {
                                 MenuEntries.Clear();
-                                MenuEntries.Add("Create System Link Session");
-                                MenuEntries.Add("Join System Link Session");
+                                MenuEntries.Add("Create LAN Session");
+                                MenuEntries.Add("Join LAN Session");
                                 MenuEntries.Add("Exit");
                                 break;
                             }
@@ -94,21 +95,9 @@ namespace NetRumble
                             }
                     }
                 }
-                // Simplified menu for non-networking build
-                state = value;
-                if (MenuEntries != null)
-                {
-                    MenuEntries.Clear();
-                    MenuEntries.Add("Exit");
-                }
             }
         }
 
-
-
-
-
-
         /// <summary>
         /// Constructs a new MainMenu object.
         /// </summary>
@@ -121,15 +110,8 @@ namespace NetRumble
             // capture current value of trial mode flag
             trialMode = Guide.IsTrialMode;
             updateState = false;
-            // Set up the simple menu
-            State = MainMenuState.Empty;
         }
 
-
-
-
-
-
         /// <summary>
         /// Updates the screen. This method checks the GameScreen.IsActive
         /// property, so the game will stop updating when the pause menu is active,
@@ -149,20 +131,17 @@ namespace NetRumble
                         break;
                     }
                 }
-                State = signedIntoLive ? MainMenuState.SignedInLive : 
+                State = signedIntoLive ? MainMenuState.SignedInLive :
                     MainMenuState.SignedInLocal;
             }
             else
             {
                 State = MainMenuState.SignedOut;
             }
-            // For non-networking build, just keep it simple
-            State = MainMenuState.Empty;
-            
+
             base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
         }
-        
-        
+
         /// <summary>
         /// Responds to user menu selections.
         /// </summary>
@@ -245,13 +224,6 @@ namespace NetRumble
                         break;
                     }
             }
-            // Simplified menu for non-networking build
-            switch (entryIndex)
-            {
-                case 0: // Exit
-                    OnCancel();
-                    break;
-            }
         }
 
         /// <summary>
@@ -264,7 +236,6 @@ namespace NetRumble
             Guide.ShowMarketplace(Gamer.SignedInGamers[0].PlayerIndex);
         }
 
-
         /// <summary>
         /// Attempt to join a session using an invite that was received.
         /// </summary>
@@ -272,11 +243,9 @@ namespace NetRumble
         {
             try
             {
-                // begin to join the game we were invited to
-                IAsyncResult asyncResult = NetworkSession.BeginJoinInvited(1, null, null);
-
-                // create the busy screen
-                NetworkBusyScreen busyScreen = new NetworkBusyScreen("Joining the session...", asyncResult);
+                // start async join for the invited game
+                var joinTask = NetworkSession.JoinInvitedAsync(Gamer.SignedInGamers);
+                var busyScreen = new NetworkBusyScreen<NetworkSession>("Joining the session...", joinTask);
                 busyScreen.OperationCompleted += InvitedSessionJoined;
                 ScreenManager.AddScreen(busyScreen);
             }
@@ -289,7 +258,6 @@ namespace NetRumble
             updateState = true;
         }
 
-
         /// <summary>
         /// When the user cancels the main menu, ask if they want to exit the sample.
         /// </summary>
@@ -301,7 +269,6 @@ namespace NetRumble
             ScreenManager.AddScreen(messageBox);
         }
 
-
         /// <summary>
         /// Event handler for when the user selects ok on the "are you sure
         /// you want to exit" message box.
@@ -311,7 +278,6 @@ namespace NetRumble
             ScreenManager.Game.Exit();
         }
 
-
         /// <summary>
         /// Screen-specific update to gamer rich presence.
         /// </summary>
@@ -326,21 +292,16 @@ namespace NetRumble
             }
         }
 
-
-
-
-
         private void QuickMatchSession()
         {
             // start the search
             try
             {
-                IAsyncResult asyncResult = NetworkSession.BeginFind(
-                    NetworkSessionType.PlayerMatch, 1, null, null, null);
+                var findTask = NetworkSession.FindAsync(
+                    NetworkSessionType.PlayerMatch, 1, new Dictionary<string, object>());
 
-                // create the busy screen
-                NetworkBusyScreen busyScreen = new NetworkBusyScreen(
-                    "Searching for a session...", asyncResult);
+                var busyScreen = new NetworkBusyScreen<AvailableNetworkSessionCollection>(
+                    "Searching for a session...", findTask);
                 busyScreen.OperationCompleted += QuickMatchSearchCompleted;
                 ScreenManager.AddScreen(busyScreen);
             }
@@ -378,12 +339,11 @@ namespace NetRumble
             // create the session
             try
             {
-                IAsyncResult asyncResult = NetworkSession.BeginCreate(sessionType, 1,
-                    World.MaximumPlayers, null, null);
+                var createTask = NetworkSession.CreateAsync(sessionType, 1,
+                    World.MaximumPlayers, 0, new Dictionary<string, object>());
 
-                // create the busy screen
-                NetworkBusyScreen busyScreen = new NetworkBusyScreen(
-                    "Creating a session...", asyncResult);
+                var busyScreen = new NetworkBusyScreen<NetworkSession>(
+                    "Creating a session...", createTask);
                 busyScreen.OperationCompleted += SessionCreated;
                 ScreenManager.AddScreen(busyScreen);
             }
@@ -412,7 +372,6 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Start searching for a session of the given type.
         /// </summary>
@@ -424,103 +383,51 @@ namespace NetRumble
                new SearchResultsScreen(sessionType);
             searchResultsScreen.ScreenManager = this.ScreenManager;
             ScreenManager.AddScreen(searchResultsScreen);
+        }
 
-            // start the search
-            try
-            {
-                IAsyncResult asyncResult = NetworkSession.BeginFind(sessionType, 1, null,
-                    null, null);
-
-                // create the busy screen
-                NetworkBusyScreen busyScreen = new NetworkBusyScreen(
-                    "Searching for a session...", asyncResult);
-                busyScreen.OperationCompleted += searchResultsScreen.SessionsFound;
-                ScreenManager.AddScreen(busyScreen);
-            }
-            catch (NetworkException ne)
-            {
-                const string message = "Failed searching for the session.";
-                MessageBoxScreen messageBox = new MessageBoxScreen(message);
-                messageBox.Accepted += FailedMessageBox;
-                messageBox.Cancelled += FailedMessageBox;
-                ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine("Failed to search for session:  " +
-                    ne.Message);
-            }
-            catch (GamerPrivilegeException gpe)
+        /// <summary>
+        /// Callback to receive the network-session search results from quick-match.
+        /// </summary>
+        void QuickMatchSearchCompleted(object sender, OperationCompletedEventArgs e)
+        {
+            var availableSessions = e.Result as AvailableNetworkSessionCollection;
+            if (e.Exception != null)
             {
-                const string message =
-                    "You do not have permission to search for a session.";
+                string message = e.Exception is GamerPrivilegeException
+                    ? "You do not have permission to search for a session."
+                    : "Failed searching for the session.";
                 MessageBoxScreen messageBox = new MessageBoxScreen(message);
                 messageBox.Accepted += FailedMessageBox;
                 messageBox.Cancelled += FailedMessageBox;
                 ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine(
-                    "Insufficient privilege to search for session:  " + gpe.Message);
+                System.Console.WriteLine("Failed to search for session:  " + e.Exception.Message);
+                return;
             }
-        }
 
-
-        /// <summary>
-        /// Callback to receive the network-session search results from quick-match.
-        /// </summary>
-        void QuickMatchSearchCompleted(object sender, OperationCompletedEventArgs e)
-        {
-            try
+            if ((availableSessions != null) && (availableSessions.Count > 0))
             {
-                AvailableNetworkSessionCollection availableSessions =
-                    NetworkSession.EndFind(e.AsyncResult);
-                if ((availableSessions != null) && (availableSessions.Count > 0))
+                try
                 {
-                    // join the session
-                    try
-                    {
-                        IAsyncResult asyncResult = NetworkSession.BeginJoin(
-                            availableSessions[0], null, null);
-
-                        // create the busy screen
-                        NetworkBusyScreen busyScreen = new NetworkBusyScreen(
-                            "Joining the session...", asyncResult);
-                        busyScreen.OperationCompleted += QuickMatchSessionJoined;
-                        ScreenManager.AddScreen(busyScreen);
-                    }
-                    catch (NetworkException ne)
-                    {
-                        const string message = "Failed joining the session.";
-                        MessageBoxScreen messageBox = new MessageBoxScreen(message);
-                        messageBox.Accepted += FailedMessageBox;
-                        messageBox.Cancelled += FailedMessageBox;
-                        ScreenManager.AddScreen(messageBox);
-
-                        System.Console.WriteLine("Failed to join session:  " +
-                            ne.Message);
-                    }
-                    catch (GamerPrivilegeException gpe)
-                    {
-                        const string message =
-                            "You do not have permission to join a session.";
-                        MessageBoxScreen messageBox = new MessageBoxScreen(message);
-                        messageBox.Accepted += FailedMessageBox;
-                        messageBox.Cancelled += FailedMessageBox;
-                        ScreenManager.AddScreen(messageBox);
-
-                        System.Console.WriteLine(
-                            "Insufficient privilege to join session:  " + gpe.Message);
-                    }
+                    var joinTask = NetworkSession.JoinAsync(availableSessions[0]);
+                    var busyScreen = new NetworkBusyScreen<NetworkSession>(
+                        "Joining the session...", joinTask);
+                    busyScreen.OperationCompleted += QuickMatchSessionJoined;
+                    ScreenManager.AddScreen(busyScreen);
                 }
-                else
+                catch (Exception ex)
                 {
-                    const string message = "No matches were found.";
+                    const string message = "Failed joining the session.";
                     MessageBoxScreen messageBox = new MessageBoxScreen(message);
                     messageBox.Accepted += FailedMessageBox;
                     messageBox.Cancelled += FailedMessageBox;
                     ScreenManager.AddScreen(messageBox);
+                    System.Console.WriteLine("Failed to join session:  " + ex.Message);
                 }
             }
-            catch (GamerPrivilegeException gpe){
-                MessageBoxScreen messageBox = new MessageBoxScreen(gpe.Message);
+            else
+            {
+                const string message = "No matches were found.";
+                MessageBoxScreen messageBox = new MessageBoxScreen(message);
                 messageBox.Accepted += FailedMessageBox;
                 messageBox.Cancelled += FailedMessageBox;
                 ScreenManager.AddScreen(messageBox);
@@ -533,33 +440,18 @@ namespace NetRumble
         /// </summary>
         void SessionCreated(object sender, OperationCompletedEventArgs e)
         {
-            NetworkSession networkSession = null;
-            try
-            {
-                networkSession = NetworkSession.EndCreate(e.AsyncResult);
-            }
-            catch (NetworkException ne)
+            var networkSession = e.Result as NetworkSession;
+            if (e.Exception != null || networkSession == null)
             {
-                const string message = "Failed creating the session.";
+                string message = e.Exception is GamerPrivilegeException
+                    ? "You do not have permission to create a session. " + e.Exception.Message
+                    : "Failed creating the session.";
                 MessageBoxScreen messageBox = new MessageBoxScreen(message);
                 messageBox.Accepted += FailedMessageBox;
                 messageBox.Cancelled += FailedMessageBox;
                 ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine("Failed to create session:  " +
-                    ne.Message);
-            }
-            catch (GamerPrivilegeException gpe)
-            {
-                const string message =
-                    "You do not have permission to create a session. ";
-                MessageBoxScreen messageBox = new MessageBoxScreen(message+gpe.Message);
-                messageBox.Accepted += FailedMessageBox;
-                messageBox.Cancelled += FailedMessageBox;
-                ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine(
-                    "Insufficient privilege to create session:  " + gpe.Message);
+                System.Console.WriteLine("Failed to create session:  " + e.Exception?.Message);
+                return;
             }
             if (networkSession != null)
             {
@@ -569,39 +461,23 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Callback when a session is quick-matched.
         /// </summary>
         void QuickMatchSessionJoined(object sender, OperationCompletedEventArgs e)
         {
-            NetworkSession networkSession = null;
-            try
-            {
-                networkSession = NetworkSession.EndJoin(e.AsyncResult);
-            }
-            catch (NetworkException ne)
+            var networkSession = e.Result as NetworkSession;
+            if (e.Exception != null || networkSession == null)
             {
-                const string message = "Failed joining the session.";
+                string message = e.Exception is GamerPrivilegeException
+                    ? "You do not have permission to join a session."
+                    : "Failed joining the session.";
                 MessageBoxScreen messageBox = new MessageBoxScreen(message);
                 messageBox.Accepted += FailedMessageBox;
                 messageBox.Cancelled += FailedMessageBox;
                 ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine("Failed to join session:  " +
-                    ne.Message);
-            }
-            catch (GamerPrivilegeException gpe)
-            {
-                const string message =
-                    "You do not have permission to join a session.";
-                MessageBoxScreen messageBox = new MessageBoxScreen(message);
-                messageBox.Accepted += FailedMessageBox;
-                messageBox.Cancelled += FailedMessageBox;
-                ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine(
-                    "Insufficient privilege to join session:  " + gpe.Message);
+                System.Console.WriteLine("Failed to join session:  " + e.Exception?.Message);
+                return;
             }
             if (networkSession != null)
             {
@@ -609,7 +485,6 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Load the lobby screen with the new session.
         /// </summary>
@@ -623,57 +498,37 @@ namespace NetRumble
             }
         }
 
-
         /// <summary>
         /// Finishes the asynchronous process of joining a game from an invitation,
         /// joining the lobby of a hosted game if the join was successful.
         /// </summary>
         void InvitedSessionJoined(object sender, OperationCompletedEventArgs e)
         {
-            NetworkSession networkSession = null;
-            try
-            {
-                networkSession = NetworkSession.EndJoinInvited(e.AsyncResult);
-            }
-            catch (NetworkSessionJoinException je)
-            {
-                const string message = "Failed joining the session (";
-                MessageBoxScreen messageBox = new MessageBoxScreen(message + je.JoinError.ToString() + ").");
-                messageBox.Accepted += FailedMessageBox;
-                messageBox.Cancelled += FailedMessageBox;
-                ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine("Failed to join session:  " +
-                    je.Message);
-            }
-            catch (Exception ge)
+            var networkSession = e.Result as NetworkSession;
+            if (e.Exception != null || networkSession == null)
             {
-                const string message = "Failed joining the session (";
-                MessageBoxScreen messageBox = new MessageBoxScreen(message + ge.Message + ").");
+                string message = e.Exception is NetworkSessionJoinException je
+                    ? "Failed joining the session (" + je.JoinError.ToString() + ")."
+                    : "Failed joining the session (" + (e.Exception?.Message ?? "Unknown error") + ").";
+                MessageBoxScreen messageBox = new MessageBoxScreen(message);
                 messageBox.Accepted += FailedMessageBox;
                 messageBox.Cancelled += FailedMessageBox;
                 ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine("Failed to join session:  " +
-                    ge.Message);
+                System.Console.WriteLine("Failed to join session:  " + e.Exception?.Message);
+                return;
             }
 
             // Start the lobby if we got the session!
             // Otherwise the MainMenuScreen will be available.
-            if (networkSession != null)
-            {
-                LobbyScreen lobbyScreen = new LobbyScreen(networkSession);
-                lobbyScreen.ScreenManager = ScreenManager;
-                ScreenManager.AddScreen(lobbyScreen);
-            }
+            LobbyScreen lobbyScreen = new LobbyScreen(networkSession);
+            lobbyScreen.ScreenManager = ScreenManager;
+            ScreenManager.AddScreen(lobbyScreen);
         }
 
-
         /// <summary>
         /// Event handler for when the user selects ok on the network-operation-failed
         /// message box.
         /// </summary>
         void FailedMessageBox(object sender, EventArgs e) { }
-
     }
-}
+}

+ 65 - 100
NetRumble/Core/Screens/NetworkBusyScreen.cs

@@ -1,131 +1,100 @@
-//-----------------------------------------------------------------------------
-// NetworkBusyScreen.cs
-//
-// Microsoft XNA Community Game Platform
-// Copyright (C) Microsoft Corporation. All rights reserved.
-//-----------------------------------------------------------------------------
 
 using System;
+using System.Threading;
+using System.Threading.Tasks;
 using Microsoft.Xna.Framework;
-using Microsoft.Xna.Framework.Content;
 using Microsoft.Xna.Framework.Graphics;
+using Microsoft.Xna.Framework.Content;
 
 namespace NetRumble
 {
-    /// <summary>
-    /// When an asynchronous network operation (for instance searching for or joining a
-    /// session) is in progress, we want to display some sort of busy indicator to let
-    /// the user know the game hasn't just locked up. We also want to make sure they
-    /// can't pick some other menu option before the current operation has finished.
-    /// This screen takes care of both requirements in a single stroke. It monitors
-    /// the IAsyncResult returned by an asynchronous network call, displaying a busy
-    /// indicator for as long as the call is still in progress. When it notices the
-    /// IAsyncResult has completed, it raises an event to let the game know it should
-    /// proceed to the next step, after which the busy screen automatically goes away.
-    /// Because this screen is on top of all others for as long as the asynchronous
-    /// operation is in progress, it automatically takes over all user input,
-    /// preventing any other menu entries being selected until the operation completes.
-    /// </summary>
-    /// <remarks>Based on a class in the Network Game State Management sample.</remarks>
-    class NetworkBusyScreen : GameScreen
+    // Modern async/await version using Task<T>
+    class NetworkBusyScreen<T> : GameScreen
     {
-
-
-        const float busyTextureScale = 0.8f;
-
-
-
-
-
-
-        /// <summary>
-        /// The message displayed in the screen.
-        /// </summary>
-        string message;
-
-        /// <summary>
-        /// The async result polled by the screen.
-        /// </summary>
-        IAsyncResult asyncResult;
-
-        /// <summary>
-        /// The rotating "activity" texture in the screen.
-        /// </summary>
+        readonly Task<T> task;
+        readonly CancellationTokenSource cts;
+        bool completionRaised;
         Texture2D busyTexture;
+        string message;
 
+        event EventHandler<OperationCompletedEventArgs> operationCompleted;
+        public event EventHandler<OperationCompletedEventArgs> OperationCompleted
+        {
+            add { operationCompleted += value; }
+            remove { operationCompleted -= value; }
+        }
 
-
-
-
-
-        public event EventHandler<OperationCompletedEventArgs> OperationCompleted;
-
-
-
-
-
-
-        /// <summary>
-        /// Constructs a network busy screen for the specified asynchronous operation.
-        /// </summary>
-        public NetworkBusyScreen(string message, IAsyncResult asyncResult)
+        public NetworkBusyScreen(string message, Task<T> task)
         {
             this.message = message;
-            this.asyncResult = asyncResult;
-
+            this.task = task;
+            this.cts = null;
             IsPopup = true;
+            TransitionOnTime = TimeSpan.FromSeconds(0.1);
+            TransitionOffTime = TimeSpan.FromSeconds(0.2);
+        }
 
+        public NetworkBusyScreen(string message, Task<T> task, CancellationTokenSource cts)
+        {
+            this.message = message;
+            this.task = task;
+            this.cts = cts;
+            IsPopup = true;
             TransitionOnTime = TimeSpan.FromSeconds(0.1);
             TransitionOffTime = TimeSpan.FromSeconds(0.2);
         }
 
-        
-        /// <summary>
-        /// Loads graphics content for this screen. This uses the shared ContentManager
-        /// provided by the Game class, so the content will remain loaded forever.
-        /// Whenever a subsequent NetworkBusyScreen tries to load this same content,
-        /// it will just get back another reference to the already loaded data.
-        /// </summary>
         public override void LoadContent()
         {
             ContentManager content = ScreenManager.Game.Content;
-
             busyTexture = content.Load<Texture2D>("Textures/chatTalking");
         }
 
-
-
-
-
-
-        /// <summary>
-        /// Updates the NetworkBusyScreen.
-        /// </summary>
-        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
-                                                       bool coveredByOtherScreen)
+        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
         {
             base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
 
+            // Optional: allow user to cancel (Esc or B)
+            if (!completionRaised && cts != null)
+            {
+                var kb = Microsoft.Xna.Framework.Input.Keyboard.GetState();
+                if (kb.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape))
+                {
+                    cts.Cancel();
+                }
+                var gp = Microsoft.Xna.Framework.Input.GamePad.GetState(Microsoft.Xna.Framework.PlayerIndex.One);
+                if (gp.IsConnected && gp.IsButtonDown(Microsoft.Xna.Framework.Input.Buttons.B))
+                {
+                    cts.Cancel();
+                }
+            }
+
             // Has our asynchronous operation completed?
-            if ((asyncResult != null) && asyncResult.IsCompleted)
+            if (!completionRaised && task != null && task.IsCompleted)
             {
-                // If so, raise the OperationCompleted event.
-                if (OperationCompleted != null)
+                object resultObject = default(T);
+                Exception error = null;
+                try
                 {
-                    OperationCompleted(this,
-                                       new OperationCompletedEventArgs(asyncResult));
+                    resultObject = task.Result;
+                }
+                catch (Exception ex)
+                {
+                    error = (task?.Exception?.GetBaseException()) ?? ex;
                 }
 
-                ExitScreen();
+                var handler = operationCompleted;
+                if (handler != null)
+                {
+                    handler(this, new OperationCompletedEventArgs(resultObject, error));
+                }
 
-                asyncResult = null;
+                completionRaised = true;
+                operationCompleted = null;
+                ExitScreen();
             }
         }
 
-
-        /// <summary>
-        /// Draws the NetworkBusyScreen.
-        /// </summary>
         public override void Draw(GameTime gameTime)
         {
             SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
@@ -140,9 +109,8 @@ namespace NetRumble
             Vector2 textSize = font.MeasureString(message);
 
             // Add enough room to spin a texture.
-            Vector2 busyTextureSize = new Vector2(busyTexture.Width * busyTextureScale);
-            Vector2 busyTextureOrigin = new Vector2(busyTexture.Width / 2, 
-                busyTexture.Height / 2);
+            Vector2 busyTextureSize = new Vector2(busyTexture.Width * 0.8f);
+            Vector2 busyTextureOrigin = new Vector2(busyTexture.Width / 2, busyTexture.Height / 2);
 
             textSize.X = Math.Max(textSize.X, busyTextureSize.X);
             textSize.Y += busyTextureSize.Y + vPad;
@@ -167,24 +135,21 @@ namespace NetRumble
             ScreenManager.DrawRectangle(backgroundRectangle, new Color((byte)0, (byte)0, (byte)0,
                 (byte)(232.0f * (float)TransitionAlpha / 255.0f)));
 
-            //spriteBatch.Begin(0,BlendState.NonPremultiplied, null, null, null);
-			spriteBatch.Begin();
+            spriteBatch.Begin();
             // Draw the message box text.
             spriteBatch.DrawString(font, message, textPosition, color);
 
-            // Draw the spinning cat progress indicator.
+            // Draw the spinning busy progress indicator.
             float busyTextureRotation = (float)gameTime.TotalGameTime.TotalSeconds * 3;
 
             Vector2 busyTexturePosition = new Vector2(textPosition.X + textSize.X / 2,
                 textPosition.Y + textSize.Y - busyTextureSize.Y / 2);
 
             spriteBatch.Draw(busyTexture, busyTexturePosition, null, color,
-                busyTextureRotation, busyTextureOrigin, busyTextureScale, 
+                busyTextureRotation, busyTextureOrigin, 0.8f, 
                 SpriteEffects.None, 0);
 
             spriteBatch.End();
         }
-
-
     }
 }

+ 55 - 77
NetRumble/Core/Screens/SearchResultsScreen.cs

@@ -59,6 +59,31 @@ namespace NetRumble
             // set the transition times
             TransitionOnTime = TimeSpan.FromSeconds(1.0);
             TransitionOffTime = TimeSpan.FromSeconds(0.0);
+
+            // Start async session search immediately
+            try
+            {
+                // You may want to adjust the parameters for your game
+                var findTask = NetworkSession.FindAsync(
+                    sessionType,
+                    1, // max local gamers
+                    null // session properties
+                );
+                var busyScreen = new NetworkBusyScreen<AvailableNetworkSessionCollection>("Searching for sessions...", findTask);
+                busyScreen.OperationCompleted += SessionsFound;
+                ScreenManager.AddScreen(busyScreen);
+            }
+            catch (Exception ex)
+            {
+                string message = ex is GamerPrivilegeException
+                    ? "You do not have permission to search for a session."
+                    : "Failed searching for the session.";
+                MessageBoxScreen messageBox = new MessageBoxScreen(message);
+                messageBox.Accepted += FailedMessageBox;
+                messageBox.Cancelled += FailedMessageBox;
+                ScreenManager.AddScreen(messageBox);
+                System.Console.WriteLine($"Failed to search for session:  {ex.Message}");
+            }
         }
 
 
@@ -114,40 +139,24 @@ namespace NetRumble
             if ((availableSessions != null) && (entryIndex >= 0) && 
                 (entryIndex < availableSessions.Count))
             {
-                // start to join
                 try
                 {
-                    IAsyncResult asyncResult = NetworkSession.BeginJoin(
-                        availableSessions[entryIndex], null, null);
-
-                    // create the busy screen
-                    NetworkBusyScreen busyScreen = new NetworkBusyScreen(
-                        "Joining the session...", asyncResult);
-                    busyScreen.OperationCompleted += LoadLobbyScreen;
+                    // Use the new async/await pattern for joining a session
+                    var joinTask = NetworkSession.JoinAsync(availableSessions[entryIndex]);
+                    var busyScreen = new NetworkBusyScreen<NetworkSession>("Joining the session...", joinTask);
+                    busyScreen.OperationCompleted += LoadLobbyScreenAsync;
                     ScreenManager.AddScreen(busyScreen);
                 }
-                catch (NetworkException ne)
+                catch (Exception ex)
                 {
-                    const string message = "Failed joining the session.";
+                    string message = ex is GamerPrivilegeException
+                        ? "You do not have permission to join a session."
+                        : "Failed joining the session.";
                     MessageBoxScreen messageBox = new MessageBoxScreen(message);
                     messageBox.Accepted += FailedMessageBox;
                     messageBox.Cancelled += FailedMessageBox;
                     ScreenManager.AddScreen(messageBox);
-
-                    System.Console.WriteLine("Failed to join session:  " +
-                        ne.Message);
-                }
-                catch (GamerPrivilegeException gpe)
-                {
-                    const string message =
-                        "You do not have permission to join a session.";
-                    MessageBoxScreen messageBox = new MessageBoxScreen(message);
-                    messageBox.Accepted += FailedMessageBox;
-                    messageBox.Cancelled += FailedMessageBox;
-                    ScreenManager.AddScreen(messageBox);
-
-                    System.Console.WriteLine(
-                        "Insufficient privilege to join session:  " + gpe.Message);
+                    System.Console.WriteLine($"Failed to join session:  {ex.Message}");
                 }
             }
         }
@@ -228,42 +237,27 @@ namespace NetRumble
 
 
         /// <summary>
-        /// Callback to receive the network-session search results.
+        /// Callback to receive the network-session search results (async/await style).
         /// </summary>
         internal void SessionsFound(object sender, OperationCompletedEventArgs e)
         {
-            try
+            // e.Result should be an AvailableNetworkSessionCollection or null
+            availableSessions = e.Result as AvailableNetworkSessionCollection;
+            if (e.Exception != null)
             {
-                availableSessions = NetworkSession.EndFind(e.AsyncResult);
-            }
-            catch (NetworkException ne)
-            {
-                const string message = "Failed searching for the session.";
+                string message = e.Exception is GamerPrivilegeException
+                    ? "You do not have permission to search for a session. " + e.Exception.Message
+                    : "Failed searching for the session.";
                 MessageBoxScreen messageBox = new MessageBoxScreen(message);
                 messageBox.Accepted += FailedMessageBox;
                 messageBox.Cancelled += FailedMessageBox;
                 ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine("Failed to search for session:  " +
-                    ne.Message);
+                System.Console.WriteLine($"Failed to search for session:  {e.Exception.Message}");
             }
-            catch (GamerPrivilegeException gpe)
-            {
-                const string message =
-                    "You do not have permission to search for a session. ";
-                MessageBoxScreen messageBox = new MessageBoxScreen(message + gpe.Message);
-                messageBox.Accepted += FailedMessageBox;
-                messageBox.Cancelled += FailedMessageBox;
-                ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine(
-                    "Insufficient privilege to search for session:  " + gpe.Message);
-            } 
             MenuEntries.Clear();
             if (availableSessions != null)
             {
-                foreach (AvailableNetworkSession availableSession in
-                    availableSessions)
+                foreach (AvailableNetworkSession availableSession in availableSessions)
                 {
                     if (availableSession.CurrentGamerCount < World.MaximumPlayers)
                     {
@@ -283,41 +277,25 @@ namespace NetRumble
         /// <summary>
         /// Callback to load the lobby screen with the new session.
         /// </summary>
-        private void LoadLobbyScreen(object sender, OperationCompletedEventArgs e)
+        // New async/await style lobby loader
+        private void LoadLobbyScreenAsync(object sender, OperationCompletedEventArgs e)
         {
-            NetworkSession networkSession = null;
-            try
-            {
-                networkSession = NetworkSession.EndJoin(e.AsyncResult);
-            }
-            catch (NetworkException ne)
+            var networkSession = e.Result as NetworkSession;
+            if (e.Exception != null || networkSession == null)
             {
-                const string message = "Failed joining session.";
+                string message = e.Exception is GamerPrivilegeException
+                    ? "You do not have permission to join a session."
+                    : "Failed joining session.";
                 MessageBoxScreen messageBox = new MessageBoxScreen(message);
                 messageBox.Accepted += FailedMessageBox;
                 messageBox.Cancelled += FailedMessageBox;
                 ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine("Failed joining session:  " + ne.Message);
-            }
-            catch (GamerPrivilegeException gpe)
-            {
-                const string message =
-                    "You do not have permission to join a session.";
-                MessageBoxScreen messageBox = new MessageBoxScreen(message);
-                messageBox.Accepted += FailedMessageBox;
-                messageBox.Cancelled += FailedMessageBox;
-                ScreenManager.AddScreen(messageBox);
-
-                System.Console.WriteLine(
-                    "Insufficient privilege to join session:  " + gpe.Message);
-            }
-            if (networkSession != null)
-            {
-                LobbyScreen lobbyScreen = new LobbyScreen(networkSession);
-                lobbyScreen.ScreenManager = this.ScreenManager;
-                ScreenManager.AddScreen(lobbyScreen);
+                System.Console.WriteLine($"Failed joining session:  {e.Exception?.Message}");
+                return;
             }
+            LobbyScreen lobbyScreen = new LobbyScreen(networkSession);
+            lobbyScreen.ScreenManager = this.ScreenManager;
+            ScreenManager.AddScreen(lobbyScreen);
         }
 
 

+ 27 - 0
NetRumble/Core/UIUtility.cs

@@ -0,0 +1,27 @@
+//-----------------------------------------------------------------------------
+// UIUtilty.cs
+//
+// Microsoft XNA Community Game Platform
+// Copyright (C) Microsoft Corporation. All rights reserved.
+//-----------------------------------------------------------------------------
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NetRumble
+{
+    public static class UIUtility
+    {
+        /// <summary>
+        /// Indicates if the game is running on a mobile platform.
+        /// </summary>
+        public readonly static bool IsMobile = OperatingSystem.IsAndroid() || OperatingSystem.IsIOS();
+
+        /// <summary>
+        /// Indicates if the game is running on a desktop platform.
+        /// </summary>
+        public readonly static bool IsDesktop = OperatingSystem.IsMacOS() || OperatingSystem.IsLinux() || OperatingSystem.IsWindows();
+
+    }
+}

+ 22 - 7
NetRumble/Platforms/Android/MainActivity.cs

@@ -4,15 +4,30 @@ using Microsoft.Xna.Framework;
 
 namespace NetRumble.Android
 {
-    [Activity(Label = "NetRumble", MainLauncher = true, Icon = "@mipmap/icon", Theme = "@style/MainTheme")]
+    [Activity(
+        Label = "@string/app_name",
+        MainLauncher = true,
+        Icon = "@drawable/icon",
+        AlwaysRetainTaskState = true,
+        LaunchMode = LaunchMode.SingleInstance,
+        ScreenOrientation = ScreenOrientation.SensorLandscape,
+        ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden
+    )]
     public class MainActivity : AndroidGameActivity
     {
-        protected override void OnCreate(Bundle savedInstanceState)
+        private NetRumbleGame _game;
+        private View _view;
+
+        protected override void OnCreate(Bundle bundle)
         {
-            base.OnCreate(savedInstanceState);
-            var game = new NetRumbleGame();
-            SetContentView((game.Services.GetService(typeof(Android.Views.View)) as Android.Views.View));
-            game.Run();
+            base.OnCreate(bundle);
+
+            _game = new NetRumbleGame();
+
+            _view = _game.Services.GetService(typeof(View)) as View;
+            SetContentView(_view);
+
+            _game.Run();
         }
     }
-}
+}

+ 2 - 7
NetRumble/Platforms/Android/NetRumble.Android.csproj

@@ -2,7 +2,7 @@
   <PropertyGroup>
     <TargetFramework>net8.0-android</TargetFramework>
     <OutputType>Exe</OutputType>
-    <AssemblyName>NetRumble.Android</AssemblyName>
+    <AssemblyName>NetRumble</AssemblyName>
     <RootNamespace>NetRumble.Android</RootNamespace>
     <AndroidManifest>AndroidManifest.xml</AndroidManifest>
     <SupportedOSPlatformVersion>28</SupportedOSPlatformVersion>
@@ -18,12 +18,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
 
 </Project>

BIN
NetRumble/Platforms/Android/Resources/drawable/icon.png


File diff suppressed because it is too large
+ 491 - 0
NetRumble/Platforms/Android/Resources/drawable/icon.svg


+ 4 - 0
NetRumble/Platforms/Android/Resources/values/strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <string name="app_name">Net Rumble</string>
+</resources>

+ 1 - 6
NetRumble/Platforms/Desktop/NetRumble.DesktopGL.csproj

@@ -19,12 +19,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
 
 </Project>

+ 1 - 6
NetRumble/Platforms/Windows/NetRumble.Windows.csproj

@@ -23,12 +23,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
 
 </Project>

+ 1 - 6
NetRumble/Platforms/iOS/NetRumble.iOS.csproj

@@ -18,12 +18,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <Content Include="..\..\Core\Content\**\*.xnb" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="..\..\Core\Content\**\*.xml" Link="Content\%(RecursiveDir)%(Filename)%(Extension)">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <MonoGameContentReference Include="..\..\Core\Content\Content.mgcb" />
   </ItemGroup>
 
 </Project>

+ 105 - 25
NetRumble/README.md

@@ -1,22 +1,37 @@
-# NetRumble
+# Net Rumble Sample
 
-NetRumble is a classic multiplayer space combat game built with MonoGame 3.8.4. Players control spaceships in an asteroid field, engaging in combat with lasers, rockets, and mines while collecting power-ups.
+Net Rumble is a two-dimensional shooter, pitting up to sixteen players against one another in an arena filled with asteroids and power-ups.
 
-## Overview
+## Table of Contents
+- [Introduction to Net Rumble](#introduction-to-net-rumble)
+- [What's New](#whats-new)
+- [Goals](#goals)
+- [System Requirements](#system-requirements)
+- [Getting Started](#getting-started)
+- [Game Controls](#game-controls)
+- [Implementation Notes](#implementation-notes)
+- [Extending Net Rumble](#extending-net-rumble)
+
+---
+
+## Introduction to Net Rumble
+Net Rumble is a complete XNA Game Studio sample game. The project comes ready to compile and run, and it's easy to customize with a little bit of C# programming. You are free to use the source code as the basis for your own XNA Game Studio game projects, and to share your work with others.
+
+Net Rumble is a two-dimensional shooter, pitting up to sixteen players against one another in an arena filled with asteroids and power-ups.
 
 This is a modernized version of the NetRumble sample from the XNA Game Studio era, updated to use:
 - .NET 8.0 modern SDK-style projects
 - MonoGame 3.8.* NuGet packages
 - Cross-platform support for Windows, DesktopGL, Android, and iOS
 
-## Features
+---
 
-- Multiplayer space combat
-- Asteroid environments with realistic physics
-- Various weapons: lasers, rockets, and mines
-- Power-ups: double laser, triple laser, and rocket enhancements
-- Particle effects and explosions
-- Cross-platform networking support
+## What's New
+The following improvements have been made in the XNA Game Studio 4.0-compatible version of Net Rumble:
+- Support for Xbox LIVE invites
+- Support for trial mode: indicator and purchase option
+- Support for rich presence
+- Audio functionality migration from XACT to SoundEffect and MediaLibrary implementation
 
 ## Supported Platforms
 
@@ -25,14 +40,36 @@ This is a modernized version of the NetRumble sample from the XNA Game Studio er
 - **Android** - `NetRumble.Android.csproj`
 - **iOS** - `NetRumble.iOS.csproj`
 
-## Prerequisites
+---
+
+## Goals
+This game demonstrates the following features:
+- System Link and LIVE multiplayer
+- Game entity and state management
+- Two-dimensional rendering with bloom post-processing effects
+
+---
+
+## System Requirements
+The Windows version of this sample requires a minimum desktop resolution of 1280×720 pixels.
+
+### Multiplayer Requirements
+System Link and Games for Windows - LIVE multiplayer require that each Windows computer participating in the game be connected to a common network. In addition, each player using Games for Windows - LIVE must have a Gamertag associated with a valid App Hub membership.
+
+> **Note:**
+> In order for two instances of Net Rumble to connect to one another, the Guid property in AssemblyInfo.cs in all projects must match. If you create a Windows project and an Xbox 360 project by using the **New Project** dialog box, you will need to copy the Guid from one project to the other in order to connect.
+
+---
+
+## Getting Started
+Follow these procedures to get started.
+
+### Prerequisites
 
 - [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) or later
 - For Android development: Android SDK and Android 13.0 (API 33) or later
 - For iOS development: macOS with Xcode and iOS 11.0 or later
 
-## Building and Running
-
 ### From Visual Studio Code
 
 1. Open the project folder in VS Code
@@ -69,7 +106,7 @@ dotnet build NetRumble.Android.csproj
 3. Build and run using F5 or Ctrl+F5
 
 
-## Project Structure
+### Project Structure
 
 The project is organized as follows:
 
@@ -93,22 +130,65 @@ The project is organized as follows:
 - **NetRumble.sln**: Solution file for Visual Studio
 - **README.md**: Project documentation
 
-## Content Pipeline
+---
+
+## Game Controls
+Net Rumble uses the following keyboard and gamepad controls.
+
+| Action                        | Keyboard Control           | Gamepad Control                  |
+|-------------------------------|----------------------------|----------------------------------|
+| Select a menu entry           | UP ARROW, DOWN ARROW       | Left thumb stick, D-Pad up/down  |
+| Accept the menu selection     | SPACEBAR, ENTER            | **A**, **START**                 |
+| Cancel the menu               | ESC                        | **B**, **BACK**                  |
+| Move the ship                 | None (gamepad required)    | Left thumb stick                 |
+| Fire the current weapon       | None (gamepad required)    | Right thumb stick                |
+| Fire a mine behind the ship   | None (gamepad required)    | Right Trigger                    |
+| Pause the game                | ESC                        | **START**, **BACK**              |
+
+---
+
+## Implementation Notes
+Note the following areas when implementing the sample.
+
+### Networking
+The flow of a networked game, from creation/join to lobby to game and back again, is implemented with screens derived from the Game State Management sample code. Packets are sent between the players, each starting with a 32-bit integer value containing the packet type, defined in an enumeration in the `World` class. Each game represents a new *World object*, and the initial state of the game is generated by the host.
+
+### Entity Management
+The entity management and collision systems in Net Rumble are simple, given the small number of objects in any game. The systems use polymorphism to ensure a consistent set of interactions between all in-game objects, all of which derive from the `GameplayObject` class.
+
+### CollectCollection
+The `CollectCollection` class allows the game loop to target `GameplayObject` objects for removal from the game without actually removing them from the list. Removing an item from a list invalidates all iterators, which would disrupt the update loop. Objects to be removed are added to the Garbage list, and when the `CollectCollection.Collect` method is called, the items in the Garbage list are removed from the main list (and the Garbage list is cleared).
+
+### Reuse of Existing Samples
+Net Rumble uses the game screen management architecture from the Game State Management sample. The `BackgroundScreen` class animates a `Starfield` object while it overlays various menus, and the `GameplayScreen` owns the `World object` object that drives gameplay.
+
+This game also uses the bloom post-processing component from the Bloom Postprocess sample. It is not added to the `Game` object's component list, as doing so would add bloom to all elements rendered by the screen management system, including the user interface elements. The `GameplayScreen` object creates and manages the component, such that the game elements are processed by the component but not the user interface.
+
+### Gameplay Constants
+You can alter gameplay in many important ways by adjusting the constant variables, which you can find in the relevant classes.
+
+---
 
-This project uses pre-built XNB content files located in the `Content/` directory. No Content.mgcb file is required as all assets are already processed and ready for use.
+## Extending Net Rumble
+There are many possible ways to improve on or extend Net Rumble.
 
-## Networking
+- Add more weapons or power-ups by using new classes that mirror the existing ones.
+- Customize the `World.GenerateWorld` function to create any number of possible level configurations. Other possibilities include adding a randomly generated maze, assuming you can guarantee that the players are never sealed away from one another.
+- Add new levels, where victory is scored and the play moves on to a different level. This will require additional game-state management code to handle the additional game flow.
+- Add interesting new strategies to the game by adding projectile interactions, such as projectiles that bounce off the walls.
+- The collision system treats most of the in-game objects as circular, leading to some graphical anomalies, such as asteroids colliding outside their visible shape. Consider using the tutorial "Collision Series 2: 2D Per-Pixel Collision" to help create a pixel-accurate collision system. Note that momentum-transferring collisions, such as those between asteroids, ships, and the walls, may be greatly complicated by this procedure.
+- Implement artificial-intelligence "bots" to fill up the gameplay session. Start by separating the control of the ship from the gamepad-handling techniques, generalizing to allow any source—gamepads, an artificial intelligence algorithm, or even network data—to control the ships.
+- Support more than one player at once per machine. One possibility would be to display both ships at once using split-screen rendering.
 
-The game supports network multiplayer using the included networking components. Players can host and join games over local networks.
+There are also many possible optimizations that you could make. Many of these were not made because the current code is simpler, and the design of the game as it is today did not require them.
 
-## Development Notes
+- The game allocates many objects on the heap during gameplay, which leads to occasional collections during gameplay. These collections can be noticeable on the Xbox 360 console. In the game as it is today, these are very slight, but it's generally good practice to make allocations before the main game loop begins. The major culprits are `ParticleSystem` objects and `Projectile` class-derived objects. One possible solution would be to add factories for these objects—factories that could recycle these objects when they are no longer needed.
+- The collision system currently checks every object against every other object. A broad-pass collision check, to narrow the list of possible colliders, should improve performance. A common broad-pass method is spatial partitioning, which should work well in a game like Net Rumble. Note that the overhead from this additional pass is not free, and with as few objects as Net Rumble has, this optimization may or may not be worthwhile. If Net Rumble was modified to be more complex, then this optimization could be significant.
+- The network session supports up to 16 players, but the current implementation may be sending too much traffic to support that many players on a high-latency connection. There are many ways to reduce the network traffic, potentially at the expense of fidelity (player warping, and so on).
+- There are many small optimizations that you could make throughout the code. In general, the greatest gains will be made on high-frequency calls; for example, reducing operations that occurred for every particle in every frame would result in a large overall reduction, because of the large number of particles used.
 
-- Uses modern .NET 8.0 SDK-style project files
-- MonoGame 3.8.* packages provide cross-platform compatibility
-- No legacy XNA Framework dependencies
-- Content files are pre-processed .xnb files for immediate use
-- Cross-platform input handling for different device types
+---
 
 ## License
 
-This project is based on the original Microsoft XNA Community Game Platform sample and is provided for educational and demonstration purposes.
+This project is based on the original Microsoft XNA Community Game Platform sample and is provided for educational and demonstration purposes.

Some files were not shown because too many files changed in this diff