Compare commits
46 Commits
Author | SHA1 | Date | |
---|---|---|---|
21077b6e53 | |||
6a3a225931 | |||
711f1e69f6 | |||
95f1df755e | |||
db334fc573 | |||
7f5e6a1fec | |||
4f47f758e7 | |||
bf546793e2 | |||
c980235726 | |||
cc443704f8 | |||
45673816bf | |||
3ee6551013 | |||
6726982ee6 | |||
0e2eb924c7 | |||
9126cbad77 | |||
279b249f2b | |||
bb8bea4562 | |||
949549caff | |||
3b5ca90cfb | |||
95d52a8866 | |||
3b9e5d2ece | |||
9145747e20 | |||
ee233c52f0 | |||
948b379f9d | |||
ddf5275a37 | |||
ce9afe339c | |||
fee56c6042 | |||
e2a8f446bb | |||
bec4209232 | |||
b93240f992 | |||
31831ff9d1 | |||
db37a8c924 | |||
11a41fdca7 | |||
f4e1d3a3b8 | |||
4caab69266 | |||
15f6a8df22 | |||
fb236647a3 | |||
5fbbfba593 | |||
84f6ca7434 | |||
9a6ba77e72 | |||
b94b469c39 | |||
6eaa057da8 | |||
7ca5c63131 | |||
70b8236f4e | |||
77965b715f | |||
1ceff14887 |
91
.github/workflows/build_stage.yml
vendored
Normal file
91
.github/workflows/build_stage.yml
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
name: Build Stage
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
godot:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [linux, win32, win64]
|
||||
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: Configure
|
||||
env:
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
run: |
|
||||
if [ "$PLATFORM" = "win32" ] || [ "$PLATFORM" = "win64" ] ; then
|
||||
echo "::set-env name=EXPORT_SUFFIX::.exe"
|
||||
fi
|
||||
- name: Build
|
||||
id: build
|
||||
uses: josephbmanley/build-godot-action@v1.4.0
|
||||
with:
|
||||
name: defend-together${{ env.EXPORT_SUFFIX }}
|
||||
preset: ${{ matrix.platform }}
|
||||
projectDir: client
|
||||
debugMode: '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-2"
|
||||
DEST_DIR: stage/cloudformation
|
||||
AWS_S3_BUCKET: dt-deployment-bucket
|
||||
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-2
|
||||
- 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,Domain=${{ secrets.DOMAIN }},environment=stage,DockerTag=stage,release=stage,PublicSubnets=${{ secrets.SUBNET_IDS }}
|
26
.github/workflows/push_dev.yml
vendored
Normal file
26
.github/workflows/push_dev.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: Push Dev
|
||||
|
||||
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-2"
|
||||
DEST_DIR: develop/cloudformation
|
||||
AWS_S3_BUCKET: dt-deployment-bucket
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
147
.github/workflows/release.yml
vendored
Normal file
147
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
name: Publish Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
godot:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [linux, win32, win64]
|
||||
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/release/$PLATFORM -O client/templates/$PLATFORM
|
||||
- name: Configure
|
||||
env:
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
run: |
|
||||
if [ "$PLATFORM" = "win32" ] || [ "$PLATFORM" = "win64" ] ; then
|
||||
echo "::set-env name=EXPORT_SUFFIX::.exe"
|
||||
fi
|
||||
- name: Build
|
||||
id: build
|
||||
uses: josephbmanley/build-godot-action@v1.4.0
|
||||
with:
|
||||
name: defend-together${{ env.EXPORT_SUFFIX }}
|
||||
preset: ${{ matrix.platform }}
|
||||
projectDir: client
|
||||
- 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-2"
|
||||
DEST_DIR: production/cloudformation
|
||||
AWS_S3_BUCKET: dt-deployment-bucket
|
||||
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-2
|
||||
- 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 }}
|
71
.github/workflows/release_server.yml
vendored
71
.github/workflows/release_server.yml
vendored
@ -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"
|
30
README.md
30
README.md
@ -1,9 +1,29 @@
|
||||
# ludum-dare-46
|
||||

|
||||

|
||||
|
||||
## Client
|
||||
# Defend Together
|
||||
|
||||
The client is build via Godot v3.2.1
|
||||

|
||||
|
||||
## Server
|
||||
Defend Together is a mutiplayer demo game project created in C++ with the [ENet Library](http://enet.bespin.org/) and [Godot v3.2.1](https://godotengine.org/)
|
||||
|
||||
The server is a C++ app built using ENet
|
||||
See the [wiki](https://github.com/josephbmanley/defend-together/wiki) for project documentation
|
||||
|
||||
Checkout the project on [Itch.io](https://josephbmanley.itch.io/defend-together)
|
||||
|
||||
View nested `ReadMe.md` files:
|
||||
- [Client](blob/master/client)
|
||||
- [Server](blob/master/server)
|
||||
---
|
||||
|
||||
This project highlights the following:
|
||||
|
||||
- Continuous Integration & Continuous Deployment
|
||||
|
||||
- UDP Server Networking
|
||||
|
||||
- Utilization of Cloud Services in development
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
1
client/.gitignore
vendored
1
client/.gitignore
vendored
@ -1 +1,2 @@
|
||||
builds
|
||||
templates
|
@ -1,4 +1,4 @@
|
||||
# LD46 Client
|
||||
# Defend Together Client
|
||||
|
||||
## Build Requirements
|
||||
|
||||
@ -6,4 +6,4 @@ Godot 3.2.1-stable
|
||||
|
||||
### Godot Modules
|
||||
|
||||
- [GDNet3](https://github.com/perdugames/gdnet3)
|
||||
- [GDNet3](https://github.com/josephbmanley/gdnet3)
|
@ -1,6 +1,6 @@
|
||||
[preset.0]
|
||||
|
||||
name="Linux/X11"
|
||||
name="linux"
|
||||
platform="Linux/X11"
|
||||
runnable=true
|
||||
custom_features=""
|
||||
@ -21,28 +21,89 @@ 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"
|
||||
custom_template/release="templates/linux"
|
||||
custom_template/debug="templates/linux"
|
||||
|
||||
[preset.1]
|
||||
|
||||
name="HTML5"
|
||||
platform="HTML5"
|
||||
name="win32"
|
||||
platform="Windows Desktop"
|
||||
runnable=true
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter=""
|
||||
export_path="builds/client.html"
|
||||
export_path=""
|
||||
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=""
|
||||
texture_format/bptc=false
|
||||
texture_format/s3tc=true
|
||||
texture_format/etc=false
|
||||
texture_format/etc2=false
|
||||
texture_format/no_bptc_fallbacks=true
|
||||
binary_format/64_bits=false
|
||||
binary_format/embed_pck=false
|
||||
custom_template/release="templates/win32"
|
||||
custom_template/debug="templates/win32"
|
||||
codesign/enable=false
|
||||
codesign/identity=""
|
||||
codesign/password=""
|
||||
codesign/timestamp=true
|
||||
codesign/timestamp_server_url=""
|
||||
codesign/digest_algorithm=1
|
||||
codesign/description=""
|
||||
codesign/custom_options=PoolStringArray( )
|
||||
application/icon=""
|
||||
application/file_version=""
|
||||
application/product_version=""
|
||||
application/company_name=""
|
||||
application/product_name=""
|
||||
application/file_description=""
|
||||
application/copyright=""
|
||||
application/trademarks=""
|
||||
|
||||
[preset.2]
|
||||
|
||||
name="win64"
|
||||
platform="Windows Desktop"
|
||||
runnable=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter=""
|
||||
export_path=""
|
||||
patch_list=PoolStringArray( )
|
||||
script_export_mode=1
|
||||
script_encryption_key=""
|
||||
|
||||
[preset.2.options]
|
||||
|
||||
texture_format/bptc=false
|
||||
texture_format/s3tc=true
|
||||
texture_format/etc=false
|
||||
texture_format/etc2=false
|
||||
texture_format/no_bptc_fallbacks=true
|
||||
binary_format/64_bits=true
|
||||
binary_format/embed_pck=false
|
||||
custom_template/release="templates/win64"
|
||||
custom_template/debug="templates/win64"
|
||||
codesign/enable=false
|
||||
codesign/identity=""
|
||||
codesign/password=""
|
||||
codesign/timestamp=true
|
||||
codesign/timestamp_server_url=""
|
||||
codesign/digest_algorithm=1
|
||||
codesign/description=""
|
||||
codesign/custom_options=PoolStringArray( )
|
||||
application/icon=""
|
||||
application/file_version=""
|
||||
application/product_version=""
|
||||
application/company_name=""
|
||||
application/product_name=""
|
||||
application/file_description=""
|
||||
application/copyright=""
|
||||
application/trademarks=""
|
||||
|
@ -1,7 +1,8 @@
|
||||
[gd_scene load_steps=6 format=2]
|
||||
[gd_scene load_steps=7 format=2]
|
||||
|
||||
[ext_resource path="res://assets/images/character/iron_player.png" type="Texture" id=1]
|
||||
[ext_resource path="res://scripts/entities/Player.gd" type="Script" id=2]
|
||||
[ext_resource path="res://scripts/entities/Camera.gd" type="Script" id=3]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id=1]
|
||||
|
||||
@ -33,3 +34,7 @@ valign = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Camera" type="Camera2D" parent="."]
|
||||
zoom = Vector2( 0.5, 0.5 )
|
||||
script = ExtResource( 3 )
|
||||
|
@ -23,6 +23,43 @@ config/icon="res://icon.png"
|
||||
|
||||
MusicManager="*res://nodes/MusicManager.tscn"
|
||||
NetworkManager="*res://nodes/NetworkManager.tscn"
|
||||
ImportantEntities="*res://scripts/singletons/ImportantEntities.gd"
|
||||
|
||||
[input]
|
||||
|
||||
ui_left={
|
||||
"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":16777231,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":14,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
ui_right={
|
||||
"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":16777233,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":15,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":68,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
ui_up={
|
||||
"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":16777232,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":87,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
ui_down={
|
||||
"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":16777234,"unicode":0,"echo":false,"script":null)
|
||||
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":13,"pressure":0.0,"pressed":false,"script":null)
|
||||
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":83,"unicode":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
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]
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
35
client/scripts/entities/Camera.gd
Normal file
35
client/scripts/entities/Camera.gd
Normal file
@ -0,0 +1,35 @@
|
||||
extends Camera2D
|
||||
|
||||
#-------------#
|
||||
# Camera Zoom #
|
||||
#-------------#
|
||||
export var zoomDecelration = 4
|
||||
export var zoomCapSpeed = 0.125
|
||||
|
||||
export var minZoom = 0.25
|
||||
export var maxZoom = 0.5
|
||||
var zoomChange = 0
|
||||
|
||||
func _ready():
|
||||
zoom.x = (minZoom + maxZoom) / 2
|
||||
zoom.y = (minZoom + maxZoom) / 2
|
||||
|
||||
func _process(delta):
|
||||
CameraZoom(delta)
|
||||
|
||||
func _input(event):
|
||||
if event is InputEventMouseButton:
|
||||
if event.is_pressed():
|
||||
if event.button_index == BUTTON_WHEEL_UP:
|
||||
zoomChange = -zoomCapSpeed
|
||||
if event.button_index == BUTTON_WHEEL_DOWN:
|
||||
zoomChange = zoomCapSpeed
|
||||
|
||||
func CameraZoom(delta):
|
||||
if(zoomChange > 0):
|
||||
zoomChange = clamp(zoomChange - zoomDecelration * delta, 0, zoomCapSpeed)
|
||||
elif(zoomChange < 0):
|
||||
zoomChange = clamp(zoomChange + zoomDecelration * delta, -zoomCapSpeed, 0)
|
||||
|
||||
zoom.x = clamp(zoom.x + zoomChange, minZoom, maxZoom)
|
||||
zoom.y = clamp(zoom.y + zoomChange, minZoom, maxZoom)
|
@ -1,23 +1,53 @@
|
||||
extends KinematicBody2D
|
||||
|
||||
const BASE_SPEED : float = 50.0
|
||||
export var user : String = ""
|
||||
var main : bool = false
|
||||
var input_locks = 0
|
||||
const SPEED_VAL = 75
|
||||
|
||||
func set_main():
|
||||
main = true
|
||||
$Label.hide()
|
||||
$Camera.show()
|
||||
$Camera.current = true
|
||||
|
||||
$"/root/ImportantEntities".main_player = self
|
||||
|
||||
func _ready():
|
||||
$Camera.hide()
|
||||
set_username("")
|
||||
|
||||
func set_username(username):
|
||||
user = username
|
||||
$Label.text = user
|
||||
#$Label.text = $"/root/NetworkManager".username
|
||||
|
||||
func update_position(pos : Vector2):
|
||||
if not main or abs(abs(pos.x) - abs(position.x)) > SPEED_VAL or abs(abs(pos.y) - abs(position.y)) > SPEED_VAL:
|
||||
position = pos
|
||||
|
||||
var velocity : Vector2 = Vector2(0,0)
|
||||
|
||||
func _process(delta):
|
||||
var movePos : Vector2 = Vector2(0,0)
|
||||
if main and input_locks <= 0:
|
||||
var relative_movement : Vector2 = Vector2(0,0)
|
||||
|
||||
if(Input.is_action_pressed("ui_up")):
|
||||
relative_movement.y = relative_movement.y - 1
|
||||
if(Input.is_action_pressed("ui_down")):
|
||||
relative_movement.y = relative_movement.y + 1
|
||||
if(Input.is_action_pressed("ui_right")):
|
||||
relative_movement.x = relative_movement.x + 1
|
||||
if(Input.is_action_pressed("ui_left")):
|
||||
relative_movement.x = relative_movement.x - 1
|
||||
|
||||
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_right")):
|
||||
movePos.x = movePos.x + 1
|
||||
if(Input.is_action_just_pressed("ui_left")):
|
||||
movePos.x = movePos.x - 1
|
||||
if relative_movement != Vector2.ZERO:
|
||||
move_and_collide(relative_movement * delta * BASE_SPEED)
|
||||
var dest = $"/root/ImportantEntities".tile_map.world_to_map(self.position)
|
||||
dest = dest + (position - (dest * 16))/16
|
||||
$"/root/NetworkManager".move_player(dest.x, dest.y)
|
||||
|
||||
if movePos != Vector2.ZERO:
|
||||
$"/root/NetworkManager".move_player(movePos.x, movePos.y)
|
||||
func lock_input():
|
||||
input_locks = input_locks + 1
|
||||
func unlock_input():
|
||||
input_locks = input_locks - 1
|
||||
|
@ -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 = ""
|
||||
|
||||
@ -21,21 +23,24 @@ var packetQueue = []
|
||||
var error_info = ""
|
||||
var world_data : String = ""
|
||||
|
||||
var last_move_time = null
|
||||
|
||||
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 +55,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,6 +78,7 @@ func process_events():
|
||||
var event_type = event.get_event_type()
|
||||
|
||||
if(event_type == GDNetEvent.RECEIVE):
|
||||
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':
|
||||
@ -85,6 +91,9 @@ func process_events():
|
||||
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 +103,27 @@ 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, pck_type = GDNetMessage.RELIABLE):
|
||||
packetQueue.append({'channel':channel, 'packet' : packet, 'type' : pck_type})
|
||||
|
||||
func move_player(x,y):
|
||||
if last_move_time == null || OS.get_ticks_msec() - last_move_time > 50:
|
||||
|
||||
var pckt : PoolByteArray = ("3|" + str(x) + "," + str(y)).to_ascii()
|
||||
|
||||
packetQueue.append(pckt)
|
||||
send_packet(pckt, 0, GDNetMessage.SEQUENCED)
|
||||
last_move_time = OS.get_ticks_msec()
|
||||
|
||||
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'], packetQueue[0]['type'])
|
||||
packetQueue.remove(0)
|
||||
|
||||
|
||||
|
4
client/scripts/singletons/ImportantEntities.gd
Normal file
4
client/scripts/singletons/ImportantEntities.gd
Normal file
@ -0,0 +1,4 @@
|
||||
extends Node
|
||||
|
||||
var main_player : KinematicBody2D = null
|
||||
var tile_map : TileMap = null
|
@ -2,6 +2,7 @@ extends Node
|
||||
|
||||
|
||||
func _ready():
|
||||
$"/root/ImportantEntities".tile_map = $Tiles
|
||||
$Tiles.clear()
|
||||
$"/root/NetworkManager".connect("world_data_recieved", self, "_on_world_update")
|
||||
$"/root/NetworkManager".request_world_map()
|
||||
@ -10,27 +11,28 @@ 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])))
|
||||
if 'player:' in tile_data[2]:
|
||||
print(tile_data)
|
||||
var player_name = tile_data[2].substr(len('player:'))
|
||||
print(player_name)
|
||||
if $"/root/NetworkManager".username == player_name:
|
||||
$Player.position = pos
|
||||
else:
|
||||
update_entity(player_name, pos, "player")
|
||||
else:
|
||||
var pos : Vector2 = $Tiles.map_to_world(Vector2(float(tile_data[0]), float(tile_data[1])))
|
||||
pos = Vector2(pos.x + get_decimals(float(tile_data[0])), pos.y + get_decimals(float(tile_data[1])))
|
||||
|
||||
#pos = Vector2(pos.x + decs.x, pos.y + decs.y)
|
||||
var entity_data = tile_data[2].split(':')
|
||||
update_entity(entity_data[1], pos, entity_data[0])
|
||||
|
||||
else:
|
||||
$Tiles.set_cell(int(tile_data[0]), int(tile_data[1]), int(tile_data[2]))
|
||||
if $Loading != null:
|
||||
if get_node_or_null("Loading") != null:
|
||||
$Loading.queue_free()
|
||||
|
||||
func get_decimals(number : float):
|
||||
var temp : float = int(number)
|
||||
return (number - temp) * 16
|
||||
|
||||
func display_error(error):
|
||||
print("Error " + error)
|
||||
|
||||
@ -43,7 +45,21 @@ func update_entity(entity_id : String, pos : Vector2, type : String):
|
||||
entity = gobj.instance()
|
||||
add_child(entity, true)
|
||||
entity.set_name(str(type + "-" + entity_id))
|
||||
if type == "player":
|
||||
if entity_id == $"/root/NetworkManager".username:
|
||||
entity.set_main()
|
||||
else:
|
||||
display_error("Trying to load entity of type: " + type + ", but failed.")
|
||||
if entity:
|
||||
if entity.has_method("update_position"):
|
||||
entity.update_position(pos)
|
||||
else:
|
||||
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()
|
||||
|
||||
|
37
client/scripts/systems/chatbox.gd
Normal file
37
client/scripts/systems/chatbox.gd
Normal file
@ -0,0 +1,37 @@
|
||||
extends Control
|
||||
|
||||
const MESSAGE_TIMEOUT : int = 17
|
||||
|
||||
var locking = false
|
||||
|
||||
func _ready():
|
||||
$"/root/NetworkManager".connect("chat_message_recieved", self, "_on_new_message")
|
||||
|
||||
func _on_new_message(message):
|
||||
print(message)
|
||||
var message_block : Label = Label.new()
|
||||
message_block.text = message
|
||||
var timer = Timer.new()
|
||||
timer.connect("timeout", message_block, "queue_free")
|
||||
message_block.add_child(timer)
|
||||
$Messages.add_child(message_block)
|
||||
timer.start(MESSAGE_TIMEOUT)
|
||||
|
||||
func _process(delta):
|
||||
|
||||
if $LineEdit.has_focus() != locking:
|
||||
locking = $LineEdit.has_focus()
|
||||
if locking:
|
||||
$"/root/ImportantEntities".main_player.lock_input()
|
||||
else:
|
||||
$"/root/ImportantEntities".main_player.unlock_input()
|
||||
|
||||
|
||||
if(Input.is_action_just_pressed("send_chat_message")):
|
||||
if $LineEdit.has_focus():
|
||||
if len($LineEdit.text) > 0:
|
||||
$"/root/NetworkManager".send_chat_message($LineEdit.text)
|
||||
$LineEdit.text = ""
|
||||
$LineEdit.release_focus()
|
||||
else:
|
||||
$LineEdit.grab_focus()
|
Submodule infrastructure/cloudformation/cluster updated: a7d7961c27...2826c2eb03
44
infrastructure/cloudformation/dt/dns.yaml
Normal file
44
infrastructure/cloudformation/dt/dns.yaml
Normal 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}."
|
69
infrastructure/cloudformation/dt/load_balancing.yaml
Normal file
69
infrastructure/cloudformation/dt/load_balancing.yaml
Normal 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
|
52
infrastructure/cloudformation/dt/task.yaml
Normal file
52
infrastructure/cloudformation/dt/task.yaml
Normal 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
|
117
infrastructure/cloudformation/dt/top.yaml
Normal file
117
infrastructure/cloudformation/dt/top.yaml
Normal 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: prod
|
||||
|
||||
#------------
|
||||
# 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/dt-deployment-bucket/${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/dt-deployment-bucket/${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/dt-deployment-bucket/${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/dt-deployment-bucket/${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
|
@ -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
|
@ -1,16 +1,31 @@
|
||||
# LD46 Server
|
||||
# Defend Together Server
|
||||
|
||||
Here are the instructions below for running the server locally on linux.
|
||||
|
||||
## Build Requirements
|
||||
|
||||
Enet development files:
|
||||
_Instructions are for Fedora Linux_
|
||||
|
||||
`dnf install enet-devel`
|
||||
Install dependencies:
|
||||
|
||||
|
||||
G++
|
||||
|
||||
`dnf install g++`
|
||||
`dnf install enet-devel g++`
|
||||
|
||||
For testing:
|
||||
|
||||
`dnf install gtest gmock gmock-devel gtest-devel`
|
||||
|
||||
## Building Server
|
||||
|
||||
Within the server directory run: `bash build.sh`
|
||||
|
||||
## Running Tests
|
||||
|
||||
Within the server directory run: `bash run_tests.sh`
|
||||
|
||||
## Running Server
|
||||
|
||||
If you have already built the server, you can run the binary location at `builds/server.out`, otherwise within the server directory, you can run `run_build.sh` to build and run the server.
|
||||
|
||||
# Running in Docker
|
||||
|
||||
A `Dockerfile` is provided in this project. So you can run `docker build -t defend-together .` and then `docker run -p 7777:7777 defend-together` to run locally on port `7777` in docker.
|
@ -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
|
@ -1,13 +1,11 @@
|
||||
#ifndef CONSOLE_HPP
|
||||
#define CONSOLE_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
bool console_is_running = false;
|
||||
#include "gameserver.hpp"
|
||||
|
||||
class ServerConsole
|
||||
{
|
||||
@ -22,19 +20,38 @@ class ServerConsole
|
||||
|
||||
void * console_logic(void *)
|
||||
{
|
||||
printf("Started server console...\n");
|
||||
std::cout << "Started server console..." << std::endl;
|
||||
|
||||
while(console_is_running)
|
||||
std::string input_string;
|
||||
|
||||
while(game_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;
|
||||
if(input_string == "stop")
|
||||
{
|
||||
game_is_running = false;
|
||||
}
|
||||
else if (input_string.length() > 2 && input_string.substr(0,3) == "say")
|
||||
{
|
||||
if(input_string.length() > 4)
|
||||
{
|
||||
gameserver::BroadcastMessage("Server: " + input_string.substr(4));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid console command!\n");
|
||||
std::cout << "Must pass a valid message!" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << std::endl
|
||||
<< "Invalid console command!" << std::endl
|
||||
<< "Valid commands are:" << std::endl
|
||||
<< "stop" << std::endl
|
||||
<< "say [message]" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -43,7 +60,7 @@ void * console_logic(void *)
|
||||
ServerConsole::ServerConsole(void)
|
||||
{
|
||||
//Intialized values
|
||||
console_is_running = true;
|
||||
game_is_running = true;
|
||||
|
||||
thread = pthread_t();
|
||||
|
||||
@ -61,7 +78,7 @@ void ServerConsole::stop(void)
|
||||
|
||||
bool ServerConsole::is_running(void)
|
||||
{
|
||||
return console_is_running;
|
||||
return game_is_running;
|
||||
}
|
||||
|
||||
#endif
|
@ -7,38 +7,60 @@
|
||||
class GameEntity
|
||||
{
|
||||
public:
|
||||
GameEntity(std::string,std::string,int,int);
|
||||
GameEntity(std::string,std::string,float,float);
|
||||
std::string get_id(void);
|
||||
std::string get_type(void);
|
||||
int get_x(void);
|
||||
int get_y(void);
|
||||
std::string set_x(int);
|
||||
std::string set_y(int);
|
||||
float get_x(void);
|
||||
float get_y(void);
|
||||
std::string set_x(float);
|
||||
std::string set_y(float);
|
||||
void set_velocity(float, float);
|
||||
std::string get_dump(void);
|
||||
bool movement_tick(void);
|
||||
private:
|
||||
int pos_x;
|
||||
int pos_y;
|
||||
float pos_x;
|
||||
float pos_y;
|
||||
float velocity_x;
|
||||
float velocity_y;
|
||||
std::string id;
|
||||
std::string type;
|
||||
|
||||
};
|
||||
|
||||
GameEntity::GameEntity(std::string entity_id, std::string entity_type, int x = 0, int y = 0)
|
||||
GameEntity::GameEntity(std::string entity_id, std::string entity_type, float x = 0, float y = 0)
|
||||
{
|
||||
id = entity_id;
|
||||
type = entity_type;
|
||||
velocity_x = 0;
|
||||
velocity_y = 0;
|
||||
this->set_x(x);
|
||||
this->set_y(y);
|
||||
std::cout << "Entity created with id: '" << id << "' at " << x << "," << y << std::endl;
|
||||
}
|
||||
|
||||
bool GameEntity::movement_tick()
|
||||
{
|
||||
if(velocity_x != 0 || velocity_y != 0)
|
||||
{
|
||||
set_x(pos_x + velocity_x);
|
||||
set_y(pos_y + velocity_y);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int GameEntity::get_x(void)
|
||||
void GameEntity::set_velocity(float x, float y)
|
||||
{
|
||||
velocity_x = x;
|
||||
velocity_y = y;
|
||||
}
|
||||
|
||||
float GameEntity::get_x(void)
|
||||
{
|
||||
return pos_x;
|
||||
}
|
||||
|
||||
int GameEntity::get_y(void)
|
||||
float GameEntity::get_y(void)
|
||||
{
|
||||
return pos_y;
|
||||
}
|
||||
@ -48,12 +70,12 @@ std::string GameEntity::get_dump(void)
|
||||
return std::to_string(pos_x) + "," + std::to_string(pos_y) + "," + type + ":" + id + '\n';
|
||||
}
|
||||
|
||||
std::string GameEntity::set_x(int x)
|
||||
std::string GameEntity::set_x(float x)
|
||||
{
|
||||
pos_x = x;
|
||||
return this -> get_dump();
|
||||
}
|
||||
std::string GameEntity::set_y(int y)
|
||||
std::string GameEntity::set_y(float y)
|
||||
{
|
||||
pos_y = y;
|
||||
return this -> get_dump();
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include "gameentity.hpp"
|
||||
|
||||
@ -15,10 +16,18 @@ class GameMap
|
||||
void set_tile(int,int,int);
|
||||
std::string get_tile_dump(void);
|
||||
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);
|
||||
std::string spawn_entity(std::string,std::string,float,float);
|
||||
std::string move_entity(std::string,std::string,float,float);
|
||||
void set_entity_velocity(std::string,std::string,float,float);
|
||||
std::string move_enitity_relatively(std::string,std::string,float,float);
|
||||
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);
|
||||
std::string world_tick(void);
|
||||
|
||||
private:
|
||||
int ** map_data;
|
||||
int size_x;
|
||||
@ -53,30 +62,78 @@ GameMap::GameMap(int x, int y)
|
||||
|
||||
}
|
||||
|
||||
std::string GameMap::spawn_entity(std::string entity_id, std::string entity_type, int x = 0, int y = 0)
|
||||
std::string GameMap::spawn_entity(std::string entity_id, std::string entity_type, float x = 0, float y = 0)
|
||||
{
|
||||
GameEntity entity = GameEntity(entity_id, entity_type, x, y);
|
||||
entities.push_back(entity);
|
||||
return entity.get_dump();
|
||||
}
|
||||
|
||||
std::string GameMap::move_entity(std::string entity_id, std::string entity_type, int x = 0, int y = 0)
|
||||
std::string GameMap::move_enitity_relatively(std::string entity_id, std::string entity_type, float x = 0, float y = 0)
|
||||
{
|
||||
for(int i = 0; i < entities.size(); i++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
entities[i].set_x(entities[i].get_x() + x);
|
||||
entities[i].set_y(entities[i].get_y() + y);
|
||||
}
|
||||
return entities[i].get_dump();
|
||||
return move_entity(entity_id, entity_type, x - entities[i].get_x(), y - entities[i].get_y());
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string GameMap::move_entity(std::string entity_id, std::string entity_type, float x = 0, float y = 0)
|
||||
{
|
||||
for(int i = 0; i < entities.size(); i++)
|
||||
{
|
||||
if(entities[i].get_id() == entity_id && entities[i].get_type() == entity_type)
|
||||
{
|
||||
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)
|
||||
{
|
||||
entity->set_x(0);
|
||||
}
|
||||
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 "";
|
||||
}
|
||||
|
||||
void GameMap::set_entity_velocity(std::string entity_id, std::string entity_type, float x = 0, float y = 0)
|
||||
{
|
||||
for(int i = 0; i < entities.size(); i++)
|
||||
{
|
||||
if(entities[i].get_id() == entity_id && entities[i].get_type() == entity_type)
|
||||
{
|
||||
GameEntity* entity = &entities[i];
|
||||
entity->set_velocity(x,y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GameMap::valid_x_y(int x, int y)
|
||||
{
|
||||
if(x < size_x && x >= 0)
|
||||
@ -89,6 +146,23 @@ bool GameMap::valid_x_y(int x, int y)
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string GameMap::world_tick(void)
|
||||
{
|
||||
std::string dump = "";
|
||||
for(int i = 0; i < entities.size(); i++)
|
||||
{
|
||||
if(entities[i].movement_tick())
|
||||
{
|
||||
dump += entities[i].get_dump();
|
||||
}
|
||||
}
|
||||
if(dump != "")
|
||||
{
|
||||
std::cout << dump << std::endl;
|
||||
}
|
||||
return dump;
|
||||
}
|
||||
|
||||
int GameMap::get_tile(int x, int y)
|
||||
{
|
||||
if(this->valid_x_y(x,y))
|
||||
@ -137,4 +211,83 @@ 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
|
@ -7,7 +7,13 @@
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "gamemap.hpp"
|
||||
using namespace std;
|
||||
|
||||
bool game_is_running = false;
|
||||
|
||||
const int SERVER_PORT = 7777;
|
||||
const int MAX_PLAYERS = 32;
|
||||
@ -21,16 +27,28 @@ static std::string usernames[MAX_PLAYERS];
|
||||
static ENetHost* server;
|
||||
|
||||
//===================
|
||||
// Network Processes
|
||||
// Game Server Logic
|
||||
//===================
|
||||
namespace gameserver
|
||||
{
|
||||
void Intialize()
|
||||
{
|
||||
printf("Starting DT game server...\n");
|
||||
|
||||
for(int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
usernames[i] = "";
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerMove(ENetEvent* event)
|
||||
{
|
||||
int peer_id = event->peer -> incomingPeerID;
|
||||
|
||||
int move_x = 0;
|
||||
int move_y = 1;
|
||||
|
||||
float move_x = 0;
|
||||
float move_y = 0;
|
||||
|
||||
//Remove id marker from packet data
|
||||
std::string data_input((char*)event->packet->data);
|
||||
data_input = data_input.substr(2);
|
||||
|
||||
@ -38,14 +56,14 @@ void PlayerMove(ENetEvent* event)
|
||||
std::stringstream ss(data_input);
|
||||
std::string tempString;
|
||||
std::getline(ss, tempString, ',');
|
||||
move_x = std::stoi(tempString);
|
||||
move_x = std::stof(tempString);
|
||||
std::getline(ss, tempString, '\n');
|
||||
move_y = std::stoi(tempString);
|
||||
move_y = std::stof(tempString);
|
||||
|
||||
//Update player position
|
||||
std::string resp = "2|" + gamemap.move_entity(usernames[peer_id], "player", move_x, move_y);
|
||||
std::string resp = "2|" + gamemap.move_enitity_relatively(usernames[peer_id], "player", move_x, move_y);
|
||||
const char* data = resp.c_str();
|
||||
ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE);
|
||||
ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, 0);
|
||||
enet_host_broadcast(server, 0, packet);
|
||||
}
|
||||
|
||||
@ -76,15 +94,14 @@ void Authenticate(ENetEvent* event)
|
||||
usernames[peer_id] = username;
|
||||
|
||||
//Spawn entity
|
||||
std::string spawn_data = gamemap.spawn_entity(usernames[peer_id], "player", 127, 127);
|
||||
std::string spawn_data = gamemap.spawn_entity(usernames[peer_id], "player", 128, 129);
|
||||
|
||||
//Tell peers about new player
|
||||
data = ("2|" + spawn_data).c_str();
|
||||
ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_host_broadcast(server, 0, packet);
|
||||
|
||||
std::cout << usernames[peer_id] << " spawned!" << std::endl;
|
||||
std::cout << peer_id << " | " << username << std::endl;
|
||||
std::cout << "'" << usernames[peer_id] << "' spawned!" << std::endl;
|
||||
|
||||
std::string okay_response("1|OK");
|
||||
data = (okay_response + username).c_str();
|
||||
@ -104,4 +121,109 @@ void Authenticate(ENetEvent* event)
|
||||
}
|
||||
}
|
||||
|
||||
void SendPlayerMessage(ENetPeer* peer, std::string message)
|
||||
{
|
||||
const char* data = message.c_str();
|
||||
|
||||
ENetPacket* packet = enet_packet_create(data, strlen(data) + 1, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_peer_send(peer, 1, packet);
|
||||
}
|
||||
|
||||
bool SendPlayerMessage(std::string username, std::string message)
|
||||
{
|
||||
for(int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
if(usernames[i] == username)
|
||||
{
|
||||
SendPlayerMessage(&(server->peers[i]), message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BroadcastMessage(std::string message)
|
||||
{
|
||||
const char* data = message.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);
|
||||
}
|
||||
|
||||
void ProcessChatMessage(ENetEvent* event)
|
||||
{
|
||||
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;
|
||||
BroadcastMessage(resp);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void HandlePlayerDisconnect(ENetEvent* event)
|
||||
{
|
||||
//Clear username data and remove entity
|
||||
std::string username = usernames[event -> peer -> incomingPeerID];
|
||||
if(username != "")
|
||||
{
|
||||
std::cout << "'" << username << "' disconnected" << std::endl;
|
||||
gamemap.remove_entity(username,"player");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessGeneralInput(ENetEvent* event)
|
||||
{
|
||||
char c [1];
|
||||
c[0] = event->packet->data[0];
|
||||
switch (c[0])
|
||||
{
|
||||
case '1':
|
||||
gameserver::Authenticate(event);
|
||||
break;
|
||||
case '2':
|
||||
gameserver::DataRequest(event);
|
||||
break;
|
||||
case '3':
|
||||
gameserver::PlayerMove(event);
|
||||
break;
|
||||
default:
|
||||
std::cout << "Invalid packet recieved!" <<std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_t ticker_thread;
|
||||
|
||||
void * GameTicker(void *)
|
||||
{
|
||||
while(game_is_running)
|
||||
{
|
||||
usleep(50000*3);
|
||||
std::string data_string = "2|"+gamemap.world_tick();
|
||||
const char* data = data_string.c_str();
|
||||
ENetPacket* packet = enet_packet_create(data, strlen(data), ENET_PACKET_FLAG_UNSEQUENCED);
|
||||
enet_host_broadcast(server, 0, packet);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StartTicker()
|
||||
{
|
||||
pthread_create(&ticker_thread, NULL, GameTicker, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -9,17 +9,11 @@
|
||||
|
||||
#include "console.hpp"
|
||||
#include "gameserver.hpp"
|
||||
using namespace std;
|
||||
|
||||
int main (int argc, char ** argv)
|
||||
{
|
||||
printf("Starting LD46 game server...\n");
|
||||
|
||||
for(int i = 0; i < MAX_PLAYERS; i++)
|
||||
{
|
||||
usernames[i] = "";
|
||||
}
|
||||
|
||||
|
||||
//Intialize Enet
|
||||
if (enet_initialize () != 0)
|
||||
{
|
||||
fprintf (stderr, "ENet server failed to initialize!\n");
|
||||
@ -27,35 +21,37 @@ int main (int argc, char ** argv)
|
||||
}
|
||||
atexit (enet_deinitialize);
|
||||
|
||||
//Intialize game data
|
||||
gameserver::Intialize();
|
||||
|
||||
//Create console
|
||||
ServerConsole console;
|
||||
|
||||
//Create ENet objects
|
||||
ENetEvent event;
|
||||
ENetAddress address;
|
||||
|
||||
|
||||
//Build Enet Host
|
||||
address.host = ENET_HOST_ANY;
|
||||
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)
|
||||
{
|
||||
std::cout << "Failed to create ENet server host!" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Start ticker
|
||||
gameserver::StartTicker();
|
||||
|
||||
//Run main game server loop
|
||||
std::cout << "Awaiting connections..." << std::endl;
|
||||
while(console_is_running)
|
||||
while(game_is_running)
|
||||
{
|
||||
ENetEvent event;
|
||||
/* Wait up to 1000 milliseconds for an event. */
|
||||
while (enet_host_service (server, & event, EVENT_RATE) > 0)
|
||||
{
|
||||
printf("Parsing event!\n");
|
||||
switch (event.type)
|
||||
{
|
||||
case ENET_EVENT_TYPE_CONNECT:
|
||||
@ -64,44 +60,26 @@ int main (int argc, char ** argv)
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
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;
|
||||
|
||||
char c [1];
|
||||
c[0] = event.packet->data[0];
|
||||
|
||||
/* Determine which function to call based on
|
||||
the value of the first character of packet */
|
||||
std::cout << "Packet Type: " << c << std::endl;
|
||||
switch (c[0])
|
||||
//Channel #0 processes authentication, world updates,
|
||||
// and player movement
|
||||
if (event.channelID == 0)
|
||||
{
|
||||
case '1':
|
||||
Authenticate(&event);
|
||||
break;
|
||||
case '2':
|
||||
DataRequest(&event);
|
||||
break;
|
||||
case '3':
|
||||
PlayerMove(&event);
|
||||
break;
|
||||
default:
|
||||
std::cout << "Invalid packet recieved!" <<std::endl;
|
||||
break;
|
||||
gameserver::ProcessGeneralInput(&event);
|
||||
}
|
||||
//Channel #1 proccesses chat messages
|
||||
else if(event.channelID == 1)
|
||||
{
|
||||
gameserver::ProcessChatMessage(&event);
|
||||
|
||||
|
||||
//Destroy packet now that it has been used
|
||||
}
|
||||
enet_packet_destroy (event.packet);
|
||||
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
std::cout << event.peer -> data << " disconnected." << std::endl;
|
||||
//Remove peer data on disconnect
|
||||
gameserver::HandlePlayerDisconnect(&event);
|
||||
|
||||
// DELETE ENTITY HERE
|
||||
|
||||
usernames[event.peer -> incomingPeerID] = "";
|
||||
//Open peer for new connection
|
||||
event.peer -> data = NULL;
|
||||
}
|
||||
}
|
||||
|
10
server/nginx.conf
Normal file
10
server/nginx.conf
Normal file
@ -0,0 +1,10 @@
|
||||
server {
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
location /health {
|
||||
return 200 'alive';
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
@ -1,4 +1,9 @@
|
||||
#!/bin/sh
|
||||
mkdir -p builds
|
||||
g++ tests.cpp -o builds/tests.out -lgtest
|
||||
if [ $? = 0 ]
|
||||
then
|
||||
./builds/tests.out
|
||||
else
|
||||
exit -1
|
||||
fi
|
@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "gameentity.hpp"
|
||||
#include "gamemap.hpp"
|
||||
@ -38,12 +39,12 @@ TEST(EntityTest, GetBasicDump)
|
||||
{
|
||||
GameEntity entity = CreateTestEntity();
|
||||
//Test intial location (0,0)
|
||||
EXPECT_EQ(entity.get_dump(), std::string("0,0," + TEST_ENTITY_TYPE + ":" + TEST_ENTITY_ID + "\n"));
|
||||
EXPECT_EQ(entity.get_dump(), std::string("0.000000,0.000000," + TEST_ENTITY_TYPE + ":" + TEST_ENTITY_ID + "\n"));
|
||||
|
||||
//Test non-intial location (1,2)
|
||||
entity.set_x(1);
|
||||
entity.set_y(2);
|
||||
EXPECT_EQ(entity.get_dump(), std::string("1,2," + TEST_ENTITY_TYPE + ":" + TEST_ENTITY_ID + "\n"));
|
||||
EXPECT_EQ(entity.get_dump(), std::string("1.000000,2.000000," + TEST_ENTITY_TYPE + ":" + TEST_ENTITY_ID + "\n"));
|
||||
}
|
||||
|
||||
//Test GameMap Object
|
||||
@ -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[])
|
||||
{
|
||||
|
Reference in New Issue
Block a user