Added UserAction to API and Chat
parent
d774f2f93c
commit
5b85fabcbd
|
|
@ -166,3 +166,6 @@ staticfiles/
|
||||||
|
|
||||||
# database
|
# database
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
|
|
||||||
|
# todo
|
||||||
|
todo*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
from .views import *
|
||||||
|
from users.models import *
|
||||||
|
|
||||||
|
def is_action_processable(request):
|
||||||
|
user = Users.objects.get(request.body["user"])
|
||||||
|
if user:
|
||||||
|
# TODO implement
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def process_action(request):
|
||||||
|
if not is_action_processable(request):
|
||||||
|
return
|
||||||
|
print(request.body)
|
||||||
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Defines Data ffs
|
||||||
|
|
||||||
|
|
||||||
|
VIEW_ACTION = 0
|
||||||
|
PURCHASE_ACTION = 1
|
||||||
|
MODIFY_ACTION = 2
|
||||||
|
EQUIP_ACTION = 3
|
||||||
|
|
||||||
|
# Need to define costs for purchase actions and requirements for modify and equip actions
|
||||||
|
|
||||||
|
# Saving space and reducing response times of the server is a priority so keeping in mind that this
|
||||||
|
# data will probably live on the RAM (as file-io is costly) so it needs to be small and easy to parse
|
||||||
|
|
||||||
|
# # sights,trg,brl
|
||||||
|
# # cost,ammo^inter^ >^skin,number
|
||||||
|
# # |----|^^|-||-|>^>^|--||------|
|
||||||
|
# WEAPON_DATA = """000000000000000000000000000000""" # Use Ints goddamit
|
||||||
|
# oh yeah, ints
|
||||||
|
WEAPON_DATA = [] # wtf
|
||||||
69
api/tests.py
69
api/tests.py
|
|
@ -1,3 +1,72 @@
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from rest_framework.test import APITestCase, APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
# Create your tests here.
|
# Create your tests here.
|
||||||
|
class UnAuthAccessTests(APITestCase):
|
||||||
|
def test_province_get(self):
|
||||||
|
response = self.client.get("/api/v1/provinces/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_assault_troops_get(self):
|
||||||
|
response = self.client.get("/api/v1/assault_troops/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_servers_get(self):
|
||||||
|
response = self.client.get("/api/v1/servers/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_players_get(self):
|
||||||
|
response = self.client.get("/api/v1/players/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_user_action_get(self):
|
||||||
|
response = self.client.get("/api/v1/user_action/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_user_action_post(self):
|
||||||
|
response = self.client.post("/api/v1/user_action/", {"user": 1, "action": "142"}, format="json")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthAccessTests(APITestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.user = User.objects.create_user(username="test", password="pass123")
|
||||||
|
self.user2 = User.objects.create_user(username="test2", password="pass123", is_staff=True)
|
||||||
|
|
||||||
|
def test_province_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/provinces/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_assault_troops_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/assault_troops/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_servers_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/servers/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_players_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/players/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_players_staff_get(self):
|
||||||
|
self.client.login(username="test2", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/players/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_user_action_post(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.post("/api/v1/user_action/", {"user": 1, "action": "142"}, format="json")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
|
||||||
|
def test_user_data_get(self):
|
||||||
|
self.client.login(username="test", password="pass123")
|
||||||
|
response = self.client.get("/api/v1/user_data/")
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,4 +12,5 @@ urlpatterns = [
|
||||||
path('servers/<int:nm>/', ServerView.as_view()),
|
path('servers/<int:nm>/', ServerView.as_view()),
|
||||||
path('user_data/', UserDataView.as_view()),
|
path('user_data/', UserDataView.as_view()),
|
||||||
path('user_data/<int:pk>/', UserDatumView.as_view()),
|
path('user_data/<int:pk>/', UserDatumView.as_view()),
|
||||||
|
path('user_action/', UserActionView.as_view()),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
16
api/views.py
16
api/views.py
|
|
@ -1,8 +1,9 @@
|
||||||
from rest_framework import generics, permissions
|
from rest_framework import generics, permissions, mixins
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from .models import *
|
from .models import *
|
||||||
from .filters import *
|
from .filters import *
|
||||||
|
from .actions import *
|
||||||
from .serializers import *
|
from .serializers import *
|
||||||
from .permissions import *
|
from .permissions import *
|
||||||
from users.models import *
|
from users.models import *
|
||||||
|
|
@ -42,7 +43,7 @@ class PlayersView(generics.ListCreateAPIView):
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class PlayerView(generics.RetrieveUpdateDestroyAPIView):
|
class PlayerView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
permission_classes = (IsStaff)
|
permission_classes = (IsStaff,)
|
||||||
queryset = Player.objects.all()
|
queryset = Player.objects.all()
|
||||||
serializer_class = PlayerSerializer
|
serializer_class = PlayerSerializer
|
||||||
|
|
||||||
|
|
@ -73,3 +74,14 @@ class UserDataView(generics.ListCreateAPIView):
|
||||||
queryset = UserData.objects.all()
|
queryset = UserData.objects.all()
|
||||||
serializer_class = UserDataSerializer
|
serializer_class = UserDataSerializer
|
||||||
filter_backends = [UserDataFilterBackend]
|
filter_backends = [UserDataFilterBackend]
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
|
class UserActionView(mixins.CreateModelMixin, generics.GenericAPIView):
|
||||||
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
queryset = UserAction.objects.all()
|
||||||
|
serializer_class = UserActionSerializer
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
process_action(request)
|
||||||
|
return self.create(request, *args, **kwargs)
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
from django import forms
|
||||||
|
from .models import Post
|
||||||
|
|
||||||
|
|
||||||
|
class PostForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Post
|
||||||
|
fields = ['content']
|
||||||
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
# from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
@ -8,7 +8,8 @@ class Blog(models.Model):
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
title = models.CharField(max_length=150)
|
title = models.CharField(max_length=150)
|
||||||
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
date_posted = models.DateTimeField(default=timezone.now)
|
# date_posted = models.DateTimeField(default=timezone.now)
|
||||||
|
date_posted = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return '/'
|
return '/'
|
||||||
|
|
@ -18,4 +19,4 @@ class Blog(models.Model):
|
||||||
class Post(models.Model):
|
class Post(models.Model):
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
author = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
date_posted = models.DateTimeField(default=timezone.now)
|
date_posted = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
|
||||||
12
blog/urls.py
12
blog/urls.py
|
|
@ -4,10 +4,12 @@ from . import views
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', views.home, name='Home'),
|
path('', views.home, name='Home'),
|
||||||
path('news/', views.news, name='News'),
|
path('news/', views.news, name='News'),
|
||||||
path('blog/<int:pk>', views.BlogDetailView.as_view(), name='Blog'),
|
# path('blog/<int:pk>', views.BlogDetailView.as_view(), name='Blog'),
|
||||||
path('blog/create/', views.BlogCreateView.as_view(), name='Blog Create'),
|
# path('blog/create/', views.BlogCreateView.as_view(), name='Blog Create'),
|
||||||
path('post/<int:pk>', views.PostDetailView.as_view(), name='Post'),
|
# path('post/<int:pk>', views.PostDetailView.as_view(), name='Post'),
|
||||||
path('post/create/', views.PostCreateView.as_view(), name='Post Create'),
|
# path('post/create/', views.PostCreateView.as_view(), name='Post Create'),
|
||||||
path('dev/', views.dev, name='Dev'),
|
# path('dev/', views.dev, name='Dev'),
|
||||||
|
path('chat/', views.chat, name='Chat'),
|
||||||
|
path("chat/p/", views.posts_partial, name="ChatPartial"),
|
||||||
path('dev/support/', views.support, name='Support'),
|
path('dev/support/', views.support, name='Support'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@ from django.shortcuts import render
|
||||||
from django.contrib.auth.mixins import *
|
from django.contrib.auth.mixins import *
|
||||||
from django.views.generic import *
|
from django.views.generic import *
|
||||||
from .models import *
|
from .models import *
|
||||||
|
from .forms import *
|
||||||
|
|
||||||
|
MAX_POSTS = 150
|
||||||
|
MARGIN = 20
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
def news(request):
|
def news(request):
|
||||||
|
|
@ -49,7 +52,7 @@ class BlogCreateView(LoginRequiredMixin, CreateView):
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.author = self.request.user
|
form.instance.author = self.request.user
|
||||||
return super().form_valid(form)
|
return super().form_valid(form) and self.request.user.is_staff
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,6 +71,39 @@ class PostCreateView(LoginRequiredMixin, CreateView):
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
def chat(request):
|
||||||
|
if request.method == "POST":
|
||||||
|
form = PostForm(request.POST)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
post = form.save(commit=False)
|
||||||
|
post.author = request.user
|
||||||
|
post.save()
|
||||||
|
print(Post.objects.count())
|
||||||
|
if Post.objects.count() > MAX_POSTS + MARGIN:
|
||||||
|
qs = Post.objects.order_by("-date_posted")
|
||||||
|
old_ids = qs.values_list("id", flat=True)[MAX_POSTS:]
|
||||||
|
|
||||||
|
if old_ids:
|
||||||
|
Post.objects.filter(id__in=old_ids).delete()
|
||||||
|
|
||||||
|
return render(request, "blog/partials/post.html", {"post": post})
|
||||||
|
|
||||||
|
posts = Post.objects.select_related("author").order_by("-date_posted")
|
||||||
|
form = PostForm()
|
||||||
|
|
||||||
|
return render(request, "blog/postList.html", {
|
||||||
|
"posts": posts,
|
||||||
|
"form": form
|
||||||
|
})
|
||||||
|
|
||||||
|
def posts_partial(request):
|
||||||
|
posts = Post.objects.select_related("author").order_by("-date_posted")[:40]
|
||||||
|
|
||||||
|
return render(request, "blog/partials/postList.html", {
|
||||||
|
"posts": posts
|
||||||
|
})
|
||||||
|
|
||||||
def dev(request):
|
def dev(request):
|
||||||
return render(request, 'dev.html', {'title': 'Development'})
|
return render(request, 'dev.html', {'title': 'Development'})
|
||||||
|
|
||||||
|
|
|
||||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
|
|
@ -512,10 +512,11 @@
|
||||||
<ul class="nav-links">
|
<ul class="nav-links">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<li><a href="/#news-section">News</a></li>
|
<li><a href="/#news-section">News</a></li>
|
||||||
|
<li><a href="{% url 'Chat' %}">Chat</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="/#hero-section">Home</a></li>
|
<li><a href="/#hero-section">Home</a></li>
|
||||||
{% endif %}
|
|
||||||
<li><a href="/#about-section">About</a></li>
|
<li><a href="/#about-section">About</a></li>
|
||||||
|
{% endif %}
|
||||||
<li><a href="/dev/support">Support Us</a></li>
|
<li><a href="/dev/support">Support Us</a></li>
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
|
|
@ -547,6 +548,7 @@
|
||||||
<!-- <a href="#" aria-label="Twitter"><i class="fab fa-twitter"></i></a> -->
|
<!-- <a href="#" aria-label="Twitter"><i class="fab fa-twitter"></i></a> -->
|
||||||
<a href="https://www.reddit.com/r/beyondheroes/" target="blank" aria-label="Reddit"><i class="fab fa-reddit"></i></a>
|
<a href="https://www.reddit.com/r/beyondheroes/" target="blank" aria-label="Reddit"><i class="fab fa-reddit"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
<p style="padding-top:10px">Beyond Heroes™ | 2026 All Rights Reserved.</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{% load markdown_extras %}
|
||||||
|
<div class="message">
|
||||||
|
<div class="message-author">{{ post.author.username }} <span style="font-size: 10px">{{ post.date_posted | date:"Y/m/d H:i" }}</span></div>
|
||||||
|
<p class="message-content">{{ post.content | markdown | safe }}</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{% for post in posts reversed %}
|
||||||
|
{% include "blog/partials/post.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
<h2 style="opacity: 80%; padding-left:40px">General Chat</h2>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.chat-container{
|
||||||
|
/* width:420px;*/
|
||||||
|
height:470px;
|
||||||
|
border-radius:12px;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messages{
|
||||||
|
flex:1;
|
||||||
|
overflow-y:auto;
|
||||||
|
padding: 10px 40px;
|
||||||
|
display:flex;
|
||||||
|
flex-direction:column;
|
||||||
|
gap:10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message{
|
||||||
|
background:#334155;
|
||||||
|
padding:2px 5px;
|
||||||
|
border-radius:2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-author{
|
||||||
|
font-size:15px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
opacity:0.7;
|
||||||
|
/* margin-bottom:4px;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 0px 5px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area{
|
||||||
|
display:flex;
|
||||||
|
height: 60px;
|
||||||
|
padding:20px 40px;
|
||||||
|
border-top:1px solid rgba(255,255,255,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area input{
|
||||||
|
flex: 1;
|
||||||
|
padding:10px;
|
||||||
|
font-size: 30px;
|
||||||
|
border-radius:8px;
|
||||||
|
border:none;
|
||||||
|
outline:none;
|
||||||
|
background:#0f172a;
|
||||||
|
color:white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-area button{
|
||||||
|
/* flex: 1;*/
|
||||||
|
margin-left:8px;
|
||||||
|
padding:10px 14px;
|
||||||
|
border:none;
|
||||||
|
border-radius:8px;
|
||||||
|
background:#3b82f6;
|
||||||
|
color:white;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="chat-container">
|
||||||
|
<div
|
||||||
|
id="post-list"
|
||||||
|
class="messages"
|
||||||
|
hx-get="{% url 'ChatPartial' %}"
|
||||||
|
hx-trigger="every 3s"
|
||||||
|
hx-swap="innerHTML">
|
||||||
|
{% include "blog/partials/postList.html" %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form
|
||||||
|
class="input-area"
|
||||||
|
id="post-form"
|
||||||
|
hx-post="{% url 'Chat' %}"
|
||||||
|
hx-target="#post-list"
|
||||||
|
hx-swap="beforeend">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.content }}
|
||||||
|
<button type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
var container = document.getElementById("post-list");
|
||||||
|
let autoScroll = true
|
||||||
|
function isAtBottom(){
|
||||||
|
return container.scrollHeight - container.scrollTop - container.clientHeight < 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.addEventListener("scroll", () => {
|
||||||
|
autoScroll = isAtBottom();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener("htmx:afterSwap", (e) => {
|
||||||
|
if(e.target.id !== "post-list") return
|
||||||
|
if(autoScroll){ container.scrollTop = container.scrollHeight; }
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 5.2.9 on 2026-03-05 12:51
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0003_userdata_delete_profile'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserAction',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('action', models.CharField(max_length=200)),
|
||||||
|
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -11,13 +11,20 @@ class UserData(models.Model):
|
||||||
equipment = models.CharField(max_length=1024, default='0;')
|
equipment = models.CharField(max_length=1024, default='0;')
|
||||||
inventory = models.CharField(max_length=1024, default='0;')
|
inventory = models.CharField(max_length=1024, default='0;')
|
||||||
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.user.username}'s data"
|
return f"{self.user.username}'s data"
|
||||||
|
|
||||||
|
|
||||||
# So this is my beutiful brainchild to keep user data in about 1KB per user, I'm not too sure but still
|
# So this is my beautiful brainchild to keep user data in about 1KB per user, I'm not too sure but still
|
||||||
# it's worth the try, so the main payload is the inventory along the soliers an their weapons and vehicles
|
# it's worth the try, so the main payload is the inventory along the soldiers and their weapons and
|
||||||
# along with the mods for everything which I have separated by `;`, `.` and `,` for soldier, equipment
|
# vehicles along with the mods for everything which I have separated by `;`, `.` and `,` for soldier,
|
||||||
# class and each equipment. I'll try a bit of bit-hacking to pack as much info of 20 bytes in 2 of the mods.
|
# equipment class and each equipment. I'll try a bit of bit-hacking to pack info of as much as 20 bytes
|
||||||
# ammo|--|sights|---|internals|---|, trigger|--|barrel|--|skins|----|, number|--------|
|
# in 2 for the mods. ammo|--|sights|---|internals|---|, trigger|--|barrel|--|skins|----|, number|--------|
|
||||||
|
|
||||||
|
|
||||||
|
class UserAction(models.Model):
|
||||||
|
user = models.OneToOneField(User, on_delete=models.CASCADE)
|
||||||
|
action = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.user.username}'s action"
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,9 @@ class UserDataSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserData
|
model = UserData
|
||||||
fields = ['user', 'name', 'xp', 'money', 'equipment', 'inventory']
|
fields = ['user', 'name', 'xp', 'money', 'equipment', 'inventory']
|
||||||
|
|
||||||
|
|
||||||
|
class UserActionSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = UserAction
|
||||||
|
fields = ['user', 'action']
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue