29 Commits

Author SHA1 Message Date
949549caff Include itch.io values 2020-05-03 22:30:01 -04:00
3b5ca90cfb Implement Client CI/CD
Enable debug export

Push build dir instead of artifact

Lots of debugging stuff

Use debug export branch

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Client pipeline debugging...

Cleanup and publish
2020-05-03 22:22:32 -04:00
95d52a8866 Client-side chat changes 2020-05-03 19:35:58 -04:00
3b9e5d2ece Debug chat issues 2020-05-03 19:34:07 -04:00
9145747e20 Implemented basic chat system 2020-05-02 17:25:31 -04:00
ee233c52f0 Added client nameplates 2020-05-02 16:36:51 -04:00
948b379f9d Client environment selection dropdown 2020-05-02 16:19:46 -04:00
ddf5275a37 Fix 2020-05-02 03:20:49 -04:00
ce9afe339c Enable dynamic port routing 2020-05-02 03:12:12 -04:00
fee56c6042 Dockerfile with working healthcheck endpoint 2020-05-02 03:02:06 -04:00
e2a8f446bb Merge 2020-05-02 01:03:29 -04:00
bec4209232 Disable dynamic routing to allow for proper healthchecks 2020-05-02 01:02:28 -04:00
b93240f992 Configure endpoint for healthcheck
Configure endpoint for healthcheck

Update healthcheck to run

HOTFIX: Fix healthcheck protocol

HOTFIX: Expose healthcheck port task

HOTFIX: Make healthcheck UDP & remove TCP endpoint

Reimplement healthcheck without forcing port

Reimplement healthcheck without forcing port
2020-05-02 00:50:16 -04:00
31831ff9d1 Reimplement healthcheck without forcing port 2020-05-02 00:31:34 -04:00
db37a8c924 Reimplement healthcheck without forcing port 2020-05-02 00:16:51 -04:00
11a41fdca7 HOTFIX: Make healthcheck UDP & remove TCP endpoint 2020-05-02 00:02:21 -04:00
f4e1d3a3b8 HOTFIX: Expose healthcheck port task 2020-05-01 23:57:09 -04:00
4caab69266 HOTFIX: Fix healthcheck protocol 2020-05-01 23:52:20 -04:00
15f6a8df22 Update healthcheck to run 2020-05-01 23:45:13 -04:00
fb236647a3 Configure endpoint for healthcheck 2020-05-01 17:43:26 -04:00
5fbbfba593 Configure endpoint for healthcheck 2020-05-01 17:42:59 -04:00
84f6ca7434 Client entity deletion 2020-05-01 02:27:47 -04:00
9a6ba77e72 Additional testing, entity deletion, and player disconnection 2020-05-01 02:24:34 -04:00
b94b469c39 INFRASTRUCTURE FIX: Container port 'tcp' -> 'udp' 2020-05-01 00:15:22 -04:00
6eaa057da8 Rework console input to use iostream 2020-05-01 00:12:57 -04:00
7ca5c63131 Reduce memory reservation 2020-05-01 00:06:06 -04:00
70b8236f4e Implement stage 2020-04-30 23:47:00 -04:00
77965b715f Fix cloudformation top stack location 2020-04-30 23:11:19 -04:00
1ceff14887 Basic infrastructure stack
Infrastructure parameter fixes

Implement working infrastructure stack in ECS
2020-04-30 23:05:13 -04:00
24 changed files with 881 additions and 148 deletions

84
.github/workflows/build_stage.yml vendored Normal file
View File

@ -0,0 +1,84 @@
name: Build Stage
on:
push:
branches:
- master
jobs:
godot:
runs-on: ubuntu-latest
strategy:
matrix:
platform: [linux]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Download Export Template
env:
PLATFORM: ${{ matrix.platform }}
run: |
mkdir -p client/templates
wget http://godot-enet.cloudsumu.com.s3-website-us-east-1.amazonaws.com/export-templates/debug/$PLATFORM -O client/templates/$PLATFORM
- name: Build
id: build
uses: josephbmanley/build-godot-action@v1.4.0
with:
name: defend-together
preset: ${{ matrix.platform }}
projectDir: client
debugMode: 'true'
package: 'true'
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: Client - ${{ matrix.platform }}
path: ${{ github.workspace }}/${{ steps.build.outputs.build }}
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Push Tag to Docker Hub
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "josephbmanley/defend-together"
TAG_NAME: "stage"
LATEST: "false"
cloudformation:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2.1.0
with:
submodules: 'true'
- name: Ship to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --follow-symlinks --delete
env:
SOURCE_DIR: infrastructure/cloudformation
AWS_REGION: "us-east-1"
DEST_DIR: dt/stage/cloudformation
AWS_S3_BUCKET: sumu-stacks
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to AWS CloudFormation
uses: aws-actions/aws-cloudformation-github-deploy@v1
with:
name: dt-infrastructure-stage
template: infrastructure/cloudformation/dt/top.yaml
capabilities: "CAPABILITY_NAMED_IAM,CAPABILITY_IAM"
parameter-overrides: VpcId=${{ secrets.VPC_ID }},SubDomain=stage.dt,Domain=${{ secrets.DOMAIN }},environment=stage,DockerTag=stage,release=stage,PublicSubnets=${{ secrets.SUBNET_IDS }}

View File

@ -0,0 +1,26 @@
name: Push Dev Infrastructure
on:
push:
branches-ignore:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2.1.0
with:
submodules: 'true'
- name: Ship to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --follow-symlinks --delete
env:
SOURCE_DIR: infrastructure/cloudformation
AWS_REGION: "us-east-1"
DEST_DIR: dt/develop/cloudformation
AWS_S3_BUCKET: sumu-stacks
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

142
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,142 @@
name: Release Server
on:
release:
types:
- created
jobs:
godot:
runs-on: ubuntu-latest
strategy:
matrix:
platform: [linux]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Download Export Template
env:
PLATFORM: ${{ matrix.platform }}
run: |
mkdir -p client/templates
wget http://godot-enet.cloudsumu.com.s3-website-us-east-1.amazonaws.com/export-templates/debug/$PLATFORM -O client/templates/$PLATFORM
- name: Build
id: build
uses: josephbmanley/build-godot-action@v1.4.0
with:
name: defend-together
preset: ${{ matrix.platform }}
projectDir: client
debugMode: 'true'
package: 'true'
- id: get_tag
name: Get Tag
env:
GITHUB_HEAD_REF: $${{ github.head_ref }}
GITHUB_BASE_REF: ${{ github.base_ref }}
run: |
TAG=$(jq --raw-output '.release.tag_name' $GITHUB_EVENT_PATH)
echo ::set-output name=TAG::$TAG
- name: Push to Itch
uses: josephbmanley/butler-publish-itchio-action@v1.0.1
env:
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
CHANNEL: ${{ matrix.platform }}
ITCH_GAME: defend-together
ITCH_USER: josephbmanley
PACKAGE: ${{ github.workspace }}/${{ steps.build.outputs.build }}
VERSION: ${{ steps.get_tag.outputs.TAG }}
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- id: get_tag
name: Get Tag
env:
GITHUB_HEAD_REF: $${{ github.head_ref }}
GITHUB_BASE_REF: ${{ github.base_ref }}
run: |
TAG=$(jq --raw-output '.release.tag_name' $GITHUB_EVENT_PATH)
echo ::set-output name=TAG::$TAG
- name: Push Tag to Docker Hub
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "josephbmanley/defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "false"
- name: Push Latest to Docker Hub
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "josephbmanley/defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "true"
- name: Push Tag to GitHub Package
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ github.actor }}
PASSWORD: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: "docker.pkg.github.com"
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "false"
- name: Push Latest to GitHub Package
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ github.actor }}
PASSWORD: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: "docker.pkg.github.com"
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "true"
cloudformation:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v2.1.0
with:
submodules: 'true'
- name: Ship to S3
uses: jakejarvis/s3-sync-action@master
with:
args: --follow-symlinks --delete
env:
SOURCE_DIR: infrastructure/cloudformation
AWS_REGION: "us-east-1"
DEST_DIR: dt/production/cloudformation
AWS_S3_BUCKET: sumu-stacks
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to AWS CloudFormation
uses: aws-actions/aws-cloudformation-github-deploy@v1
with:
name: dt-infrastructure
template: infrastructure/cloudformation/dt/top.yaml
capabilities: "CAPABILITY_NAMED_IAM,CAPABILITY_IAM"
parameter-overrides: VpcId=${{ secrets.VPC_ID }},Domain=${{ secrets.DOMAIN }},PublicSubnets=${{ secrets.SUBNET_IDS }}

View File

@ -1,71 +0,0 @@
name: Release Server
on:
release:
types:
- created
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- id: get_tag
name: Get Tag
env:
GITHUB_HEAD_REF: $${{ github.head_ref }}
GITHUB_BASE_REF: ${{ github.base_ref }}
run: |
TAG=$(jq --raw-output '.release.tag_name' $GITHUB_EVENT_PATH)
echo ::set-output name=TAG::$TAG
- name: Push Tag to Docker Hub
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "josephbmanley/defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "false"
- name: Push Latest to Docker Hub
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ secrets.DOCKER_USERNAME }}
PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "josephbmanley/defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "true"
- name: Push Tag to GitHub Package
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ github.actor }}
PASSWORD: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: "docker.pkg.github.com"
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "false"
- name: Push Latest to GitHub Package
uses: opspresso/action-docker@master
with:
args: --docker
env:
USERNAME: ${{ github.actor }}
PASSWORD: ${{ secrets.GITHUB_TOKEN }}
REGISTRY: "docker.pkg.github.com"
BUILD_PATH: "server"
DOCKERFILE: "server/Dockerfile"
IMAGE_NAME: "defend-together"
TAG_NAME: ${{ steps.get_tag.outputs.TAG }}
LATEST: "true"

1
client/.gitignore vendored
View File

@ -1 +1,2 @@
builds
templates

View File

@ -1,6 +1,6 @@
[preset.0]
name="Linux/X11"
name="linux"
platform="Linux/X11"
runnable=true
custom_features=""
@ -21,28 +21,5 @@ texture_format/etc2=false
texture_format/no_bptc_fallbacks=true
binary_format/64_bits=true
binary_format/embed_pck=false
custom_template/release="/var/home/vetra/Projects/godot-enet/bin/godot.x11.opt.64"
custom_template/debug="/var/home/vetra/Projects/godot-enet/bin/godot.x11.opt.debug.64"
[preset.1]
name="HTML5"
platform="HTML5"
runnable=true
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="builds/client.html"
patch_list=PoolStringArray( )
script_export_mode=1
script_encryption_key=""
[preset.1.options]
vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false
html/custom_html_shell=""
html/head_include=""
custom_template/release=""
custom_template/debug=""
custom_template/release="templates/linux"
custom_template/debug="templates/linux"

View File

@ -24,6 +24,14 @@ config/icon="res://icon.png"
MusicManager="*res://nodes/MusicManager.tscn"
NetworkManager="*res://nodes/NetworkManager.tscn"
[input]
send_chat_message={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777221,"unicode":0,"echo":false,"script":null)
]
}
[rendering]
environment/default_environment="res://default_env.tres"

Binary file not shown.

Binary file not shown.

View File

@ -3,17 +3,21 @@ extends KinematicBody2D
export var user : String = ""
func _ready():
set_username("")
func set_username(username):
print(username)
user = username
$Label.text = user
#$Label.text = $"/root/NetworkManager".username
func _process(delta):
var movePos : Vector2 = Vector2(0,0)
if(Input.is_action_just_pressed("ui_up")):
movePos.y = movePos.y + 1
if(Input.is_action_just_pressed("ui_down")):
movePos.y = movePos.y - 1
if(Input.is_action_just_pressed("ui_down")):
movePos.y = movePos.y + 1
if(Input.is_action_just_pressed("ui_right")):
movePos.x = movePos.x + 1
if(Input.is_action_just_pressed("ui_left")):

View File

@ -1,6 +1,7 @@
extends Node
const SERVER_HOST : String = "192.168.1.34"
var server_host : String = "127.0.0.1"
const SERVER_PORT : int = 7777
signal disconnection
@ -9,6 +10,7 @@ signal error_occured
signal logged_in
signal world_data_recieved
signal chat_message_recieved
var username : String = ""
@ -27,15 +29,17 @@ var connection_timer : Timer
func _init():
server_address = GDNetAddress.new()
server_address.set_host(SERVER_HOST)
server_address.set_port(SERVER_PORT)
client = GDNetHost.new()
client.set_max_channels(2)
client.set_max_peers(1)
client.set_event_wait(250)
client.bind()
func connect_to_server():
if peer:
peer.disconnect_now()
server_address.set_host(server_host)
server_address.set_port(SERVER_PORT)
peer = client.host_connect(server_address)
if not connection_timer:
connection_timer = Timer.new()
@ -50,13 +54,13 @@ func timeout_check():
func request_world_map():
var request_packet : PoolByteArray = "2|".to_ascii()
packetQueue.append(request_packet)
send_packet(request_packet)
func connect_as_user(username : String):
connect_to_server()
var username_packet : PoolByteArray = ("1|" + username).to_ascii()
packetQueue.append(username_packet)
send_packet(username_packet)
func display_error(error = "Unknown error occured!"):
error_info = error
@ -73,18 +77,22 @@ func process_events():
var event_type = event.get_event_type()
if(event_type == GDNetEvent.RECEIVE):
var ascii_data : String = str(event.get_packet().get_string_from_ascii())
if len(ascii_data) > 0:
if ascii_data[0] == '1':
if ascii_data.substr(2,2) == "OK":
username = ascii_data.substr(4)
print("Logged in as: " + username)
emit_signal("logged_in")
else:
display_error("Username not accepted! Reason: " + ascii_data.substr(2))
elif ascii_data[0] == '2':
world_data = ascii_data.substr(2)
emit_signal("world_data_recieved")
if event.get_channel_id() == 0:
var ascii_data : String = str(event.get_packet().get_string_from_ascii())
if len(ascii_data) > 0:
if ascii_data[0] == '1':
if ascii_data.substr(2,2) == "OK":
username = ascii_data.substr(4)
print("Logged in as: " + username)
emit_signal("logged_in")
else:
display_error("Username not accepted! Reason: " + ascii_data.substr(2))
elif ascii_data[0] == '2':
world_data = ascii_data.substr(2)
emit_signal("world_data_recieved")
elif event.get_channel_id() == 1:
var chat_message : String = str(event.get_packet().get_string_from_ascii())
emit_signal("chat_message_recieved", chat_message)
elif(event_type == GDNetEvent.CONNECT):
print("Connected to server with hostname: " + server_address.get_host() + ":" + str(server_address.get_port()))
connected = true
@ -94,16 +102,24 @@ func process_events():
connected = false
emit_signal("disconnection")
func send_chat_message(msg : String):
var pckt : PoolByteArray = (msg + '\n').to_ascii()
send_packet(pckt, 1)
func send_packet(packet : PoolByteArray, channel = 0):
packetQueue.append({'channel':channel, 'packet' : packet})
func move_player(x,y):
var pckt : PoolByteArray = ("3|" + str(x) + "," + str(y)).to_ascii()
packetQueue.append(pckt)
send_packet(pckt)
func _process(delta):
process_events()
if len(packetQueue) > 0 and connected:
peer.send_packet(packetQueue[0], 0, GDNetMessage.RELIABLE)
peer.send_packet(packetQueue[0]['packet'], packetQueue[0]['channel'], GDNetMessage.RELIABLE)
packetQueue.remove(0)

View File

@ -10,7 +10,10 @@ func _on_world_update():
var data = $"/root/NetworkManager".world_data.split('\n')
for tileUpdate in data:
if len(tileUpdate) > 3:
if ',' in tileUpdate:
if "delete," in tileUpdate:
var delete_data = tileUpdate.substr(len("delete,")).split(':')
delete_entity(delete_data[1],delete_data[0])
elif ',' in tileUpdate:
var tile_data = tileUpdate.split(',')
if ':' in tile_data[2]:
var pos : Vector2 = $Tiles.map_to_world(Vector2(int(tile_data[0]), int(tile_data[1])))
@ -47,3 +50,11 @@ func update_entity(entity_id : String, pos : Vector2, type : String):
display_error("Trying to load entity of type: " + type + ", but failed.")
if entity:
entity.position = pos
if entity.has_method("set_username"):
entity.set_username(entity_id)
func delete_entity(entity_id : String, type : String):
var entity : Node2D = get_node_or_null( str(type + "-" + entity_id))
if entity:
entity.queue_free()

View File

@ -0,0 +1,44 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: DT DNS stack
Parameters:
#------------------------
# Deployment Information
#------------------------
environment:
Type: String
Description: Name of the environment
Default: production
#-----------------------
# Route53 Configuration
#-----------------------
Domain:
Type: String
Description: The HostedZoneName to create the endpoint on
SubDomain:
Type: String
Description: The subdomain to be used by dt
#-----------
# Resources
#-----------
DtDNS:
Type: String
Description: Load balancer dns name for dt endpoint
Resources:
DtEndpoint:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: !Sub "${Domain}."
Comment: 'DNS name for dt'
Name: !Sub "${SubDomain}.${Domain}."
Type: CNAME
TTL: '300'
ResourceRecords:
- !Ref DtDNS
Outputs:
Endpoint:
Description: 'DNS name for Defend Together'
Value: !Sub "${SubDomain}.${Domain}."

View File

@ -0,0 +1,69 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: DT load balancing stack
Parameters:
environment:
Type: String
Description: Name of the environment
Default: production
release:
Type: String
Description: Name of the release name of the stack version to use.
Default: production
PublicSubnets:
Description: The public subnets for the ALB to run in.
Type: String
VpcId:
Description: ID of the VPC
Type: AWS::EC2::VPC::Id
Resources:
#-- Network Load Balancer --#
PublicNLB:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Type: network
LoadBalancerAttributes:
- Key: deletion_protection.enabled
Value: false
Scheme: internet-facing
Subnets: !Split [",", !Ref PublicSubnets]
Tags:
- Key: Name
Value: !Sub "DT-${environment}-NLB"
- Key: environment
Value: !Ref environment
NlbTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckProtocol: TCP
HealthCheckPort: 80
Port: 7777
Protocol: TCP_UDP
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '20'
VpcId: !Ref 'VpcId'
Tags:
- Key: Name
Value: !Sub 'DT-${release}'
NlbListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref NlbTargetGroup
LoadBalancerArn: !Ref PublicNLB
Port: 7777
Protocol: TCP_UDP
Outputs:
TargetGroup:
Description: ""
Value: !Ref NlbTargetGroup
NlbDnsName:
Description: ""
Value: !GetAtt PublicNLB.DNSName

View File

@ -0,0 +1,52 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: Defend Togeher ECS Task
Parameters:
LogGroupName:
Type: String
Description: The AWS CloudWatch log group to output logs to.
Default: "/ecs/dt"
environment:
Type: String
Description: Name of the environment to use in naming.
Default: production
DockerTag:
Description: Tag in DockerHub to deploy
Type: String
Default: "latest"
Resources:
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
RetentionInDays: 7
LogGroupName: !Sub "${LogGroupName}/${environment}"
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
- Name: defend-together
Essential: 'true'
Image: !Sub "josephbmanley/defend-together:${DockerTag}"
MemoryReservation: 250
PortMappings:
- HostPort: 7777
ContainerPort: 7777
Protocol: udp
- HostPort: 80
ContainerPort: 80
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region:
Ref: AWS::Region
awslogs-group:
Ref: LogGroup
Outputs:
TaskArn:
Description: ARN of the TaskDefinition
Value: !Ref TaskDefinition

View File

@ -0,0 +1,117 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: Defend Together ECS Infrastructure
Parameters:
#------------------------
# Deployment Information
#------------------------
environment:
Type: String
Description: Name of the environment to use in naming.
Default: production
release:
Type: String
Description: Name of the release name of the stack version to use.
Default: production
AllowedValues: ['develop', 'stage', 'production']
ConstraintDescription: "Must be a possible release version."
VpcId:
Description: ID of the VPC
Type: AWS::EC2::VPC::Id
DockerTag:
Description: Tag in DockerHub to deploy
Type: String
Default: "latest"
#-----------------
# Load Balancing
#-----------------
PublicSubnets:
Description: The public subnets for the ALB to run in. (Space seperated)
Type: String
#-----
# DNS
#-----
Domain:
Type: String
Description: The domain to create the endpoint on (Must have an existing hosted zone ex. `example.com`) Leave blank to skip DNS.
Default: ""
SubDomain:
Type: String
Description: The subdomain to be used by dt. (ex. `dt.example.com`)
Default: dt
#------------
# CloudWatch
#------------
LogGroup:
Type: String
Description: The AWS CloudWatch log group to output logs to.
Default: "/ecs/dt"
Conditions:
CreateDns: !Not [!Equals [!Ref Domain, ""]]
Resources:
#-----
# DNS
#-----
DnsRecords:
Condition: CreateDns
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub 'https://s3.${AWS::Region}.amazonaws.com/sumu-stacks/dt/${release}/cloudformation/dt/dns.yaml'
Parameters:
environment: !Ref environment
Domain: !Ref Domain
SubDomain: !Ref SubDomain
DtDNS: !GetAtt LoadBalancing.Outputs.NlbDnsName
#-----------------
# Load Balancing
#-----------------
LoadBalancing:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub 'https://s3.${AWS::Region}.amazonaws.com/sumu-stacks/dt/${release}/cloudformation/dt/load_balancing.yaml'
Parameters:
environment: !Ref environment
release: !Ref release
VpcId: !Ref VpcId
PublicSubnets: !Join [",", !Split [" ", !Ref PublicSubnets]]
EcsCluster:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub 'https://s3.${AWS::Region}.amazonaws.com/sumu-stacks/dt/${release}/cloudformation/cluster/top.yaml'
Parameters:
Environment: !Ref environment
VpcId: !Ref VpcId
SubnetIds: !Join [",", !Split [" ", !Ref PublicSubnets]]
Project: "DT"
#-------------------
# ECS Task & Service
#-------------------
TaskDefinition:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: !Sub 'https://s3.${AWS::Region}.amazonaws.com/sumu-stacks/dt/${release}/cloudformation/dt/task.yaml'
Parameters:
environment: !Ref environment
LogGroupName: !Ref LogGroup
DockerTag: !Ref DockerTag
EcsService:
DependsOn: LoadBalancing
Type: AWS::ECS::Service
Properties:
Cluster: !GetAtt EcsCluster.Outputs.Cluster
DesiredCount: 1
TaskDefinition: !GetAtt TaskDefinition.Outputs.TaskArn
LoadBalancers:
- ContainerName: "defend-together"
ContainerPort: 7777
TargetGroupArn: !GetAtt LoadBalancing.Outputs.TargetGroup

View File

@ -1,9 +1,15 @@
FROM fedora:32
RUN /bin/bash -c "dnf install g++ enet-devel gtest gmock gmock-devel gtest-devel -y"
# Build and setup app
RUN dnf install g++ enet-devel gtest gmock gmock-devel gtest-devel nginx -y
ADD / /dt
RUN cd /dt; /dt/build.sh
CMD ["/dt/builds/server.out"]
RUN echo "Container is healthy!" > /usr/share/nginx/html/index.html
CMD /usr/sbin/nginx ; /dt/builds/server.out
EXPOSE 80
EXPOSE 7777/udp

View File

@ -8,3 +8,10 @@ if [ -f builds/server.out ]; then
fi
g++ main.cpp -o builds/server.out -lenet -lpthread
if [ $? = 0 ]
then
echo "Build successfully!"
else
exit -1
fi

View File

@ -1,9 +1,9 @@
#ifndef CONSOLE_HPP
#define CONSOLE_HPP
#include <stdio.h>
#include <pthread.h>
#include <iostream>
#include <string>
#include <cstring>
@ -22,19 +22,22 @@ class ServerConsole
void * console_logic(void *)
{
printf("Started server console...\n");
std::cout << "Started server console..." << std::endl;
std::string input_string;
while(console_is_running)
{
char c [256];
fgets(c,sizeof(c), stdin);
if(c[0] == 'q')
if(std::getline(std::cin, input_string))
{
console_is_running = false;
}
else
{
printf("Invalid console command!\n");
if(input_string == "stop")
{
console_is_running = false;
}
else
{
std::cout << "Invalid console command!" << std::endl;
}
}
}
return 0;

View File

@ -17,8 +17,13 @@ class GameMap
std::string get_entity_dump(void);
std::string spawn_entity(std::string,std::string,int,int);
std::string move_entity(std::string,std::string,int,int);
int get_entity_pos_x(std::string,std::string);
int get_entity_pos_y(std::string,std::string);
int get_size_x(void);
int get_size_y(void);
bool entity_exists(std::string, std::string);
bool remove_entity(std::string, std::string);
private:
int ** map_data;
int size_x;
@ -66,12 +71,36 @@ std::string GameMap::move_entity(std::string entity_id, std::string entity_type,
{
if(entities[i].get_id() == entity_id && entities[i].get_type() == entity_type)
{
if(map_data[entities[i].get_x() + x][entities[i].get_y() + y] % 2 == 1)
GameEntity* entity = &entities[i];
//if(map_data[entities[i].get_x() + x][entities[i].get_y() + y] % 2 == 1)
//{
if(entity->get_x() + x < 0)
{
entities[i].set_x(entities[i].get_x() + x);
entities[i].set_y(entities[i].get_y() + y);
entity->set_x(0);
}
return entities[i].get_dump();
else if(entity->get_x() + x >= this->get_size_x())
{
entity->set_x(this->get_size_x() - 1);
}
else
{
entity->set_x(entities[i].get_x() + x);
}
if(entity->get_y() + y < 0)
{
entity->set_y(0);
}
else if(entity->get_y() + y >= this->get_size_y())
{
entity->set_y(this->get_size_y() - 1);
}
else
{
entity->set_y(entities[i].get_y() + y);
}
return entity->get_dump();
}
}
return "";
@ -137,4 +166,84 @@ int GameMap::get_size_y(void)
return size_y;
}
bool GameMap::entity_exists(std::string entity_id, std::string entity_type)
{
for(int i = 0; i < entities.size(); i++)
{
GameEntity* entity = &entities[i];
if(entity->get_id() == entity_id)
{
if(entity->get_type() == entity_type)
{
return true;
}
}
}
return false;
}
bool erase(std::vector<GameEntity> &v, GameEntity key)
{
for(auto it = v.begin(); it != v.end();)
{
if (it->get_id() == key.get_id())
{
it = v.erase(it);
return true;
}
else
{
++it;
}
}
return false;
}
bool GameMap::remove_entity(std::string entity_id, std::string entity_type)
{
for(int i = 0; i < entities.size(); i++)
{
if(entities[i].get_id() == entity_id && entities[i].get_type() == entity_type)
{
return erase(entities, entities[i]);
}
}
return false;
}
int GameMap::get_entity_pos_x(std::string entity_id, std::string entity_type)
{
for(int i = 0; i < entities.size(); i++)
{
GameEntity* entity = &entities[i];
if(entity->get_id() == entity_id)
{
if(entity->get_type() == entity_type)
{
return entity->get_x();
}
}
}
return -1;
}
int GameMap::get_entity_pos_y(std::string entity_id, std::string entity_type)
{
for(int i = 0; i < entities.size(); i++)
{
GameEntity* entity = &entities[i];
if(entity->get_id() == entity_id)
{
if(entity->get_type() == entity_type)
{
return entity->get_y();
}
}
}
return -1;
}
#endif

View File

@ -39,7 +39,7 @@ int main (int argc, char ** argv)
address.port = SERVER_PORT;
const int CHANNEL_COUNT = 1;
const int CHANNEL_COUNT = 2;
server = enet_host_create (&address, MAX_PLAYERS, CHANNEL_COUNT, 0, 0);
if (server == NULL)
@ -67,7 +67,8 @@ int main (int argc, char ** argv)
std::cout << "A packet of length " << event.packet -> dataLength << " containing "
<< event.packet -> data << " was received from " << event.peer -> data << " on channel"
<< event.channelID << std::endl;
if (event.channelID == 0)
{
char c [1];
c[0] = event.packet->data[0];
@ -89,19 +90,49 @@ int main (int argc, char ** argv)
std::cout << "Invalid packet recieved!" <<std::endl;
break;
}
//Destroy packet now that it has been used
enet_packet_destroy (event.packet);
}
else if(event.channelID == 1)
{
int peer_id = event.peer -> incomingPeerID;
if(usernames[peer_id] != "")
{
//Parse input string
std::stringstream ss((char*)event.packet->data);
std::string chat_message;
std::getline(ss, chat_message, '\n');
std::string resp = "<" + usernames[peer_id] + "> " + chat_message;
const char* data = resp.c_str();
std::cout << data << std::endl;
ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(server, 1, packet);
}
enet_packet_destroy (event.packet);
}
break;
case ENET_EVENT_TYPE_DISCONNECT:
std::cout << event.peer -> data << " disconnected." << std::endl;
//Remove peer data on disconnect
// DELETE ENTITY HERE
//Clear username data and remove entity
std::string username = usernames[event.peer -> incomingPeerID];
if(username != "")
{
std::cout << "Removing '" << username << "'s player data!" << std::endl;
gamemap.remove_entity(username,"player");
usernames[event.peer -> incomingPeerID] = "";
usernames[event.peer -> incomingPeerID] = "";
std::string resp = "2|delete,player:" + username;
const char* data = resp.c_str();
ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE);
enet_host_broadcast(server, 0, packet);
}
//Open peer for new connection
event.peer -> data = NULL;
}
}

10
server/nginx.conf Normal file
View File

@ -0,0 +1,10 @@
server {
location / {
root /usr/share/nginx/html;
}
location /health {
return 200 'alive';
add_header Content-Type text/plain;
}
}

View File

@ -1,4 +1,9 @@
#!/bin/sh
mkdir -p builds
g++ tests.cpp -o builds/tests.out -lgtest
./builds/tests.out
if [ $? = 0 ]
then
./builds/tests.out
else
exit -1
fi

View File

@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include <string>
#include <iostream>
#include "gameentity.hpp"
#include "gamemap.hpp"
@ -61,6 +62,87 @@ TEST(GameMapTest, CheckMapSize)
EXPECT_EQ(map.get_size_x(), 16);
EXPECT_EQ(map.get_size_y(), 4);
}
TEST(GameMapTest, CreateEntity)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE);
EXPECT_TRUE(map.entity_exists(TEST_ENTITY_ID, TEST_ENTITY_TYPE));
}
TEST(GameMapTest, IntialEntityPos)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 0,0);
EXPECT_EQ(map.get_entity_pos_x(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 0);
EXPECT_EQ(map.get_entity_pos_y(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 0);
}
TEST(GameMapTest, MoveEntityFromZero)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 0,0);
//Move entity
map.move_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 2, 1);
EXPECT_EQ(map.get_entity_pos_x(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 2);
EXPECT_EQ(map.get_entity_pos_y(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 1);
}
TEST(GameMapTest, MoveEntityRelative)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 0,0);
//Move entity
map.move_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 2, 1);
//Move entity again!
map.move_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 1, 1);
EXPECT_EQ(map.get_entity_pos_x(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 3);
EXPECT_EQ(map.get_entity_pos_y(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 2);
}
TEST(GameMapTest, MoveEntityNegativeFromZero)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 0, 0);
//Move entity
map.move_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, -1, -1);
EXPECT_EQ(map.get_entity_pos_x(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 0);
EXPECT_EQ(map.get_entity_pos_y(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 0);
}
TEST(GameMapTest, MoveEntityNegativeFromNonZero)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 1, 1);
//Move entity
std::cout << std::string(map.move_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, -2, -2)) << std::endl;
EXPECT_EQ(map.get_entity_pos_x(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 0);
EXPECT_EQ(map.get_entity_pos_y(TEST_ENTITY_ID, TEST_ENTITY_TYPE), 0);
}
TEST(GameMapTest, StopEntityFromMovingOutOfBounds)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, map.get_size_x()-1, map.get_size_y()-1);
//Move entity
map.move_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE, 2, 2);
EXPECT_EQ(map.get_entity_pos_x(TEST_ENTITY_ID, TEST_ENTITY_TYPE), map.get_size_x() - 1);
EXPECT_EQ(map.get_entity_pos_y(TEST_ENTITY_ID, TEST_ENTITY_TYPE), map.get_size_y() - 1);
}
TEST(GameMapTest, RemoveMapEntity)
{
GameMap map = CreateMapEntity();
map.spawn_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE);
EXPECT_TRUE(map.remove_entity(TEST_ENTITY_ID, TEST_ENTITY_TYPE));
EXPECT_FALSE(map.entity_exists(TEST_ENTITY_ID, TEST_ENTITY_TYPE));
}
int main(int argc, char* argv[])
{