code:
from datetime import datetime

from kivymd.app import MDApp
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.button import MDFlatButton, MDRectangleFlatButton
from kivymd.uix.dialog import MDDialog
from kivymd.uix.gridlayout import MDGridLayout
from kivymd.uix.label import MDLabel
from kivymd.uix.pickers import MDDatePicker
from kivymd.uix.scrollview import ScrollView
from kivymd.uix.tab import MDTabs
from kivymd.uix.tab import MDTabsBase
from kivymd.uix.textfield import MDTextField
from kivymd.uix.menu import MDDropdownMenu
from kivymd.uix.button import MDRectangleFlatIconButton

import controller
from Tests import *
from Views import *
from controller import ControllerDB

class Tab(MDBoxLayout, MDTabsBase):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class MainApp(MDApp):
    dialog = None

    data_tables = None
    d2 = None
    d3 = None
    d4 = None
    d5 = None
    ids = dict()
    controller = ControllerDB("postgresql://postgres:postgres@localhost:5432/journal")

    entries = []
    students = []
    cabinets = []
    laborants = []
    directions = []

    menu = None

    b1 = None
    b2 = None

    def __init__(self):
        super().__init__()

    def build(self):
        self.theme_cls.theme_style = "Dark"
        self.theme_cls.primary_palette = "Orange"
        tabs = MDTabs()

        self.data_tables = EntriesViews(self.entries).view()
        self.data_tables.bind(on_row_press=self.show_entry)
        b1 = MDBoxLayout(orientation="horizontal", size_hint=[1, 0.076], padding=dp(0.5), spacing=dp(10))
        self.b1 = b1
        sc1 = ScrollView()
        sc1.add_widget(self.data_tables)
        t1 = Tab(title="Записи", orientation="vertical")

        b1.add_widget(MDFlatButton(text="Добавить",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.add_entry))

        b1.add_widget(MDFlatButton(text="Фильтр_Каб",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.select_cabinet))

        b1.add_widget(MDFlatButton(text="Фильтр_Студ",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.select_student))

        b1.add_widget(MDFlatButton(text="Фильтр_Лаб",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=lambda m: self.select_laborant(m, 'bottom')))

        b1.add_widget(MDFlatButton(text="Фильтр_Комп",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.select_comp))

        b1.add_widget(MDRectangleFlatIconButton(icon="magnify",
                                                theme_icon_color="Custom",
                                                icon_color="orange",
                                                line_color="orange",
                                                text="Поиск",
                                                text_color="orange",
                                                font_size=dp(15),
                                                on_release=self.filter_entry))

        t1.add_widget(b1)
        t1.add_widget(sc1)

        t2 = Tab(title="Студенты", orientation="vertical")
        self.d2 = StudentsViews(self.students).view()
        self.d2.bind(on_row_press=self.show_student)
        b2 = MDBoxLayout(orientation="horizontal", size_hint=[1, 0.076], padding=dp(2), spacing=dp(10))
        self.b2 = b2

        b2.add_widget(MDFlatButton(text="Добавить",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.add_student))

        b2.add_widget(MDFlatButton(text="Фильтр_Напр",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.select_direction))

        b2.add_widget(MDRectangleFlatIconButton(icon="magnify",
                                                theme_icon_color="Custom",
                                                icon_color="orange",
                                                line_color="orange",
                                                text="Поиск",
                                                text_color="orange",
                                                font_size=dp(15),
                                                on_release=self.filter_student))

        sc2 = ScrollView()
        sc2.add_widget(self.d2)
        t2.add_widget(b2)
        t2.add_widget(sc2)

        t3 = Tab(title="Кабинеты", orientation="vertical")
        self.d3 = CabinetsViews(self.cabinets).view()
        self.d3.bind(on_row_press=self.show_cabinet)
        b3 = MDBoxLayout(orientation="horizontal", size_hint=[1, 0.076], padding=dp(2), spacing=dp(10))

        b3.add_widget(MDFlatButton(text="Добавить",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.add_cabinet))

        t3.add_widget(b3)

        sc3 = ScrollView()
        sc3.add_widget(self.d3)
        t3.add_widget(sc3)

        t4 = Tab(title="Лаборанты", orientation="vertical")
        self.d4 = LaborantsViews(self.laborants).view()
        self.d4.bind(on_row_press=self.show_laborant)
        b4 = MDBoxLayout(orientation="horizontal", size_hint=[1, 0.076], padding=dp(2), spacing=dp(10))

        b4.add_widget(MDFlatButton(text="Добавить",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.add_laborant))

        sc4 = ScrollView()
        sc4.add_widget(self.d4)
        t4.add_widget(b4)
        t4.add_widget(sc4)

        t5 = Tab(title="Направления", orientation="vertical")
        self.d5 = DirectionsViews(self.directions).view()
        self.d5.bind(on_row_press=self.show_direction)
        b5 = MDBoxLayout(orientation="horizontal", size_hint=[1, 0.076], padding=dp(2), spacing=dp(10))

        b5.add_widget(MDFlatButton(text="Добавить",
                                   theme_text_color="Custom",
                                   text_color=self.theme_cls.primary_color,
                                   line_color="orange",
                                   on_release=self.add_direction))

        sc5 = ScrollView()
        sc5.add_widget(self.d5)
        t5.add_widget(b5)
        t5.add_widget(sc5)

        tabs.add_widget(t1)
        tabs.add_widget(t2)
        tabs.add_widget(t3)
        tabs.add_widget(t4)
        tabs.add_widget(t5)

        self.update_data()

        return tabs

    def filter_student(self, instance):
        if self.b2.children[1].text == 'Фильтр_Напр':
            self.d2.row_data = [i.to_row() for i in self.students]
        else:
            f = self.b2.children[1].text
            self.d2.row_data = map(lambda m: m.to_row(), filter(lambda m: m.direction.name == f, self.students))

    def filter_entry(self, instance):
        ans = self.entries
        if self.b1.children[-2].text == 'Фильтр_Каб' and self.b1.children[-3].text == 'Фильтр_Студ' and self.b1.children[-4].text == 'Фильтр_Лаб' and self.b1.children[-5].text == 'Фильтр_Комп':
            self.data_tables.row_data = [i.to_row() for i in self.entries]
            return

        if self.b1.children[-2].text != 'Фильтр_Каб':
            f = self.b1.children[-2].text
            ans = filter(lambda m: m.cabinet.name == f, ans)
        if self.b1.children[-3].text != 'Фильтр_Студ':
            f = self.b1.children[-3].text
            ans = filter(lambda m: m.student.name == f, ans)
        if self.b1.children[-4].text != 'Фильтр_Лаб':
            f = self.b1.children[-4].text
            ans = filter(lambda m: m.laborant.second_name == f, ans)
        if self.b1.children[-5].text != 'Фильтр_Комп':
            f = self.b1.children[-5].text
            ans = filter(lambda m: str(m.comp_num) == f, ans)

        self.data_tables.row_data = map(lambda m: m.to_row(), ans)

    def update_data(self):
        self.entries = self.controller.get_all_entries()
        self.cabinets = self.controller.get_all_cabinets()
        self.directions = self.controller.get_all_directions()
        self.students = self.controller.get_all_students()
        self.laborants = self.controller.get_all_laborants()
        self.data_tables.row_data = [i.to_row() for i in self.entries]
        self.d2.row_data = [i.to_row() for i in self.students]
        self.d3.row_data = [i.to_row() for i in self.cabinets]
        self.d4.row_data = [i.to_row() for i in self.laborants]
        self.d5.row_data = [i.to_row() for i in self.directions]

    def select_cabinet(self, instance):
        self.menu = MDDropdownMenu()
        menu_items = [
            {
                "text": i.name,
                "on_release": lambda x=i.name: self.set_x(x, instance),
            } for i in self.cabinets]
        menu_items.insert(0, {"text": "-", "on_release": lambda x="-": self.set_x("Фильтр_Каб", instance)})
        self.menu.items = menu_items
        self.menu.caller = instance
        self.menu.position = "bottom"
        self.menu.open()

    def select_student(self, instance):
        self.menu = MDDropdownMenu()
        menu_items = [
            {
                "text": i.name,
                "on_release": lambda x=i.name: self.set_x(x, instance),
            } for i in self.students]
        menu_items.insert(0, {"text": "-", "on_release": lambda x="-": self.set_x("Фильтр_Студ", instance)})
        self.menu.items = menu_items
        self.menu.caller = instance
        self.menu.position = "bottom"
        self.menu.open()

    def select_direction(self, instance):
        self.menu = MDDropdownMenu()
        menu_items = [
            {
                "text": i.name,
                "on_release": lambda x=i.name: self.set_x(x, instance),
            } for i in self.directions]
        menu_items.insert(0, {"text": "-", "on_release": lambda x="-": self.set_x("Фильтр_Напр", instance)})
        self.menu.items = menu_items
        self.menu.caller = instance
        self.menu.position = "bottom"
        self.menu.open()

    def select_laborant(self, instance, pos):
        self.menu = MDDropdownMenu()
        menu_items = [
            {
                "text": i.second_name,
                "on_release": lambda x=i.second_name: self.set_x(x, instance),
            } for i in self.laborants]
        menu_items.insert(0, {"text": "-", "on_release": lambda x="-": self.set_x("Фильтр_Лаб", instance)})
        self.menu.items = menu_items
        self.menu.caller = instance
        self.menu.position = pos
        self.menu.open()

    def select_comp(self, instance):
        self.menu = MDDropdownMenu()
        menu_items = [
            {
                "text": str(i),
                "on_release": lambda x=str(i): self.set_x(x, instance),
            } for i in range(1, 16)]
        menu_items.insert(0, {"text": "-", "on_release": lambda x="-": self.set_x("Фильтр_Комп", instance)})
        self.menu.items = menu_items
        self.menu.caller = instance
        self.menu.position = "bottom"
        self.menu.open()

    def set_x(self, x, instance):
        instance.text = x
        self.menu.dismiss()

    def show_entry(self, instance_table, instance_row):
        self.dialog = None
        if not self.dialog:
            index = instance_row.index
            cols_num = len(instance_table.column_data)
            row_num = int(index / cols_num)

            s = self.data_tables.row_data[row_num]
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(

                    MDLabel(text="ID"),
                    MDLabel(
                        text=s[0],
                    ),
                    MDLabel(text="Номер пары"),
                    MDTextField(
                        text=s[1],
                    ),
                    MDLabel(text="Номер компьютера"),
                    MDTextField(
                        text=s[2],
                    ),
                    MDLabel(text="Фамилия студента"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        text=s[3],
                        on_release=self.select_student
                    ),
                    MDLabel(text="Дата"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        text=s[4],
                        on_release=self.show_date_picker
                    ),
                    MDLabel(text="Фамилия лаборанта"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        text=s[5],
                        on_release=lambda m: self.select_laborant(m, 'top')
                    ),
                    MDLabel(text="Название кабинета"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        text=s[6],
                        on_release=self.select_cabinet
                    ),
                    rows=7,
                    spacing="5dp",
                    size_hint_y=None,
                    height="500dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.save_entry,
                    ),
                    MDFlatButton(
                        text="Удалить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=lambda m: self.delete_entry(m, int(s[0])),
                    ),
                ],
            )
        self.dialog.open()

    def delete_entry(self, instance, id: int):
        self.controller.delete_entry(id)
        self.update_data()
        self.dialog.dismiss()

    def on_save(self, _instance, value, _date_range, button):
        button.text = str(value)

    def show_date_picker(self, instance):
        date1 = datetime.strptime(instance.text, "%Y-%m-%d").date()
        date_dialog = MDDatePicker(year=date1.year, month=date1.month, day=date1.day)
        date_dialog.bind(on_save=lambda ins, val, drange: self.on_save(ins, val, drange, instance))
        date_dialog.open()

    def close(self, instance):
        self.dialog.dismiss()

    def save_entry(self, instance):
        id = int(self.dialog.content_cls.children[-2].text)
        lesson_num = int(self.dialog.content_cls.children[-4].text)
        comp_num = int(self.dialog.content_cls.children[-6].text)
        student_name = self.dialog.content_cls.children[-8].text
        lesson_date = datetime.strptime(self.dialog.content_cls.children[-10].text, "%Y-%m-%d").date()
        laborant_name = self.dialog.content_cls.children[-12].text
        cabinet_name = self.dialog.content_cls.children[-14].text

        with self.controller.session() as s:
            laborant = s.query(controller.Laborant).filter_by(second_name=laborant_name).one()
            cabinet = s.query(controller.Cabinet).filter_by(name=cabinet_name).one()
            student = s.query(controller.Student).filter_by(name=student_name).one()
            entry = controller.Entry(id=id, cabinet_id=cabinet.id, student_id=student.id, laborant_id=laborant.id,
                                     comp_num=comp_num, lesson_num=lesson_num, lesson_date=lesson_date)
            s.merge(entry)
            s.commit()

        self.update_data()

        self.dialog.dismiss()

    def create_entry(self, instance):
        lesson_num = int(self.dialog.content_cls.children[-2].text)
        comp_num = int(self.dialog.content_cls.children[-4].text)
        student_name = self.dialog.content_cls.children[-6].text
        lesson_date = datetime.strptime(self.dialog.content_cls.children[-8].text, "%Y-%m-%d").date()
        laborant_name = self.dialog.content_cls.children[-10].text
        cabinet_name = self.dialog.content_cls.children[-12].text

        with self.controller.session() as s:
            laborant = s.query(controller.Laborant).filter_by(second_name=laborant_name).one().to_model()
            cabinet = s.query(controller.Cabinet).filter_by(name=cabinet_name).one().to_model()
            student = s.query(controller.Student).filter_by(name=student_name).one().to_model()
            entry = Entry(cabinet=cabinet, student=student, laborant=laborant,
                          comp_num=comp_num, lesson_num=lesson_num, lesson_date=lesson_date)
            self.controller.create_entry(entry)
            s.commit()

        self.update_data()

        self.dialog.dismiss()

    def add_entry(self, instance):
        self.dialog = None
        if not self.dialog:
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(
                    MDLabel(text="Номер пары"),
                    MDTextField(),
                    MDLabel(text="Номер компьютера"),
                    MDTextField(),
                    MDLabel(text="Фамилия студента"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        on_release=self.select_student
                    ),
                    MDLabel(text="Дата"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        text=str(date.today()),
                        on_release=self.show_date_picker
                    ),
                    MDLabel(text="Фамилия лаборанта"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        on_release=lambda m: self.select_laborant(m, 'top')
                    ),
                    MDLabel(text="Название кабинета"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        on_release=self.select_cabinet
                    ),
                    rows=7,
                    spacing="5dp",
                    size_hint_y=None,
                    height="500dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Отменить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Добавить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.create_entry,
                    ),
                ],
            )
        self.dialog.open()

    def add_to_db(self):
        lesson_num = self.dialog.content_cls.children[-2].text
        comp_num = self.dialog.content_cls.children[-4].text
        student_name = self.dialog.content_cls.children[-6].text
        lesson_date = self.dialog.content_cls.children[-8].text
        laborant_name = self.dialog.content_cls.children[-10].text
        cabinet_name = self.dialog.content_cls.children[-12].text

        self.dialog.dismiss()

    def show_student(self, instance_table, instance_row):
        self.dialog = None
        if not self.dialog:
            index = instance_row.index
            cols_num = len(instance_table.column_data)
            row_num = int(index / cols_num)

            s = self.d2.row_data[row_num]
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(

                    MDLabel(text="ID"),
                    MDLabel(
                        text=s[0],
                    ),
                    MDLabel(text="Фамилия студента"),
                    MDTextField(
                        text=s[1],
                    ),
                    MDLabel(text="Название направления"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        text=s[2],
                        on_release=self.select_direction
                    ),

                    rows=3,
                    spacing="5dp",
                    size_hint_y=None,
                    height="300dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.save_student,
                    ),
                    MDFlatButton(
                        text="Удалить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=lambda m: self.delete_student(m, int(s[0])),
                    ),
                ],
            )
        self.dialog.open()

    def delete_student(self, instance, id: int):
        self.controller.delete_student(id)
        self.update_data()
        self.dialog.dismiss()

    def close_student(self, instance):
        self.dialog.dismiss()

    def create_student(self, instance):
        name = self.dialog.content_cls.children[-2].text
        direction_name = self.dialog.content_cls.children[-4].text
        with self.controller.session() as s:
            direction = s.query(controller.Direction).filter_by(name=direction_name).one()
            student = Student(name=name, direction=direction.to_model())
            self.controller.create_student(student)
        self.update_data()
        self.dialog.dismiss()

    def add_student(self, instance):
        self.dialog = None
        if not self.dialog:
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(
                    MDLabel(text="Фамилия студента"),
                    MDTextField(),
                    MDLabel(text="Название направления"),
                    MDRectangleFlatButton(
                        line_color="orange",
                        on_release=self.select_direction
                    ),

                    rows=3,
                    spacing="5dp",
                    size_hint_y=None,
                    height="300dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.create_student,
                    ),
                ],
            )
        self.dialog.open()

    def save_student(self, instance):
        id = int(self.dialog.content_cls.children[-2].text)
        name = self.dialog.content_cls.children[-4].text
        direction_name = self.dialog.content_cls.children[-6].text

        with self.controller.session() as s:
            direction = s.query(controller.Direction).filter_by(name=direction_name).one()
            entry = controller.Student(id=id, name=name, direction_id=direction.id)
            s.merge(entry)
            s.commit()

        self.update_data()

        self.dialog.dismiss()

    def show_cabinet(self, instance_table, instance_row):
        self.dialog = None
        if not self.dialog:
            index = instance_row.index
            cols_num = len(instance_table.column_data)
            row_num = int(index / cols_num)

            s = self.d3.row_data[row_num]
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(

                    MDLabel(text="ID"),
                    MDLabel(
                        text=s[0],
                    ),
                    MDLabel(text="Название кабинета"),
                    MDTextField(
                        text=s[1],
                    ),

                    rows=2,
                    spacing="5dp",
                    size_hint_y=None,
                    height="200dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.save_cabinet,
                    ),
                    MDFlatButton(
                        text="Удалить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=lambda m: self.delete_cabinet(m, int(s[0])),
                    ),
                ],
            )
        self.dialog.open()

    def delete_cabinet(self, instance, id: int):
        self.controller.delete_cabinet(id)
        self.update_data()
        self.dialog.dismiss()

    def close_cabinet(self, instance):
        self.dialog.dismiss()

    def save_cabinet(self, instance):
        id = int(self.dialog.content_cls.children[-2].text)
        name = self.dialog.content_cls.children[-4].text

        with self.controller.session() as s:
            entry = controller.Cabinet(id=id, name=name)
            s.merge(entry)
            s.commit()

        self.update_data()

        self.dialog.dismiss()

    def create_cabinet(self, instance):
        name = self.dialog.content_cls.children[-2].text
        cabinet = Cabinet(name=name)
        self.controller.create_cabinet(cabinet)
        self.update_data()
        self.dialog.dismiss()

    def add_cabinet(self, instance_table):
        self.dialog = None
        if not self.dialog:
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(

                    MDLabel(text="Название кабинета"),
                    MDTextField(

                    ),

                    rows=2,
                    spacing="5dp",
                    size_hint_y=None,
                    height="200dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.create_cabinet,
                    )

                ],
            )
        self.dialog.open()

    def show_laborant(self, instance_table, instance_row):
        self.dialog = None
        if not self.dialog:
            index = instance_row.index
            cols_num = len(instance_table.column_data)
            row_num = int(index / cols_num)

            s = self.d4.row_data[row_num]
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(

                    MDLabel(text="ID"),
                    MDLabel(
                        text=s[0],
                    ),
                    MDLabel(text="Фамилия лаборанта"),
                    MDTextField(
                        text=s[1],
                    ),

                    rows=2,
                    spacing="5dp",
                    size_hint_y=None,
                    height="200dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.save_laborant,
                    ),
                    MDFlatButton(
                        text="Удалить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=lambda m: self.delete_laborant(m, int(s[0])),
                    ),
                ],
            )
        self.dialog.open()

    def delete_laborant(self, instance, id: int):
        self.controller.delete_laborant(id)
        self.update_data()
        self.dialog.dismiss()

    def close_laborant(self, instance):
        self.dialog.dismiss()

    def save_laborant(self, instance):
        id = int(self.dialog.content_cls.children[-2].text)
        second_name = self.dialog.content_cls.children[-4].text

        with self.controller.session() as s:
            entry = controller.Laborant(id=id, second_name=second_name)
            s.merge(entry)
            s.commit()

        self.update_data()

        self.dialog.dismiss()

    def create_laborant(self, instance):
        second_name = self.dialog.content_cls.children[-2].text
        laborant = Laborant(second_name=second_name)
        self.controller.create_laborant(laborant)
        self.update_data()
        self.dialog.dismiss()

    def add_laborant(self, instance):
        self.dialog = None
        if not self.dialog:
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(
                    MDLabel(text="Фамилия лаборанта"),
                    MDTextField(
                        text='',
                    ),

                    rows=2,
                    spacing="5dp",
                    size_hint_y=None,
                    height="200dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.create_laborant,
                    ),
                ],
            )
        self.dialog.open()

    def show_direction(self, instance_table, instance_row):
        self.dialog = None
        if not self.dialog:
            index = instance_row.index
            cols_num = len(instance_table.column_data)
            row_num = int(index / cols_num)

            s = self.d5.row_data[row_num]
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(

                    MDLabel(text="ID"),
                    MDLabel(
                        text=s[0],
                    ),
                    MDLabel(text="Название направления"),
                    MDTextField(
                        text=s[1],
                    ),

                    rows=2,
                    spacing="5dp",
                    size_hint_y=None,
                    height="200dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.save_direction,
                    ),
                    MDFlatButton(
                        text="Удалить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=lambda m: self.delete_direction(m, s[0]),
                    ),
                ],
            )
        self.dialog.open()

    def delete_direction(self, instance, id: int):
        self.controller.delete_direction(id)
        self.update_data()
        self.dialog.dismiss()

    def close_direction(self, instance):
        self.dialog.dismiss()

    def save_direction(self, instance):
        id = int(self.dialog.content_cls.children[-2].text)
        name = self.dialog.content_cls.children[-4].text

        with self.controller.session() as s:
            entry = controller.Direction(id=id, name=name)
            s.merge(entry)
            s.commit()

        self.update_data()

        self.dialog.dismiss()

    def create_direction(self, instance):
        name = self.dialog.content_cls.children[-2].text
        direction = Direction(name=name)
        self.controller.create_direction(direction)
        self.update_data()
        self.dialog.dismiss()

    def add_direction(self, instance):
        self.dialog = None
        if not self.dialog:
            self.dialog = MDDialog(
                title="Корректирование данных",
                type="custom",
                content_cls=MDGridLayout(
                    MDLabel(text="Название направления"),
                    MDTextField(),

                    rows=2,
                    spacing="5dp",
                    size_hint_y=None,
                    height="200dp",
                ),
                buttons=[
                    MDFlatButton(
                        text="Закрыть",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.close,
                    ),
                    MDFlatButton(
                        text="Сохранить",
                        theme_text_color="Custom",
                        text_color=self.theme_cls.primary_color,
                        on_release=self.create_direction,
                    ),
                ],
            )
        self.dialog.open()

MainApp().run()


code:
from datetime import date

class Cabinet:
    def __init__(self, id: int = None, name: str = None):
        self.id = id
        self.name = name

    def __iter__(self):
        yield 'id', self.id
        yield 'name', self.name

    def to_row(self):
        return str(self.id), self.name

class Direction:
    def __init__(self, id: int = None, name: str = None):
        self.id = id
        self.name = name

    def __iter__(self):
        yield 'id', self.id
        yield 'name', self.name

    def to_row(self):
        return str(self.id), self.name

class Laborant:
    def __init__(self, id: int = None, second_name: str = None):
        self.id = id
        self.second_name = second_name

    def __iter__(self):
        yield 'id', self.id
        yield 'second_name', self.second_name

    def to_row(self):
        return str(self.id), str(self.second_name)

class Student:
    def __init__(self, id: int = None, name: str = None, direction: Direction = None):
        self.id = id
        self.name = name
        self.direction = direction

    def __iter__(self):
        yield 'id', self.id,
        yield 'name', self.name
        yield 'direction_id', self.direction.id

    def to_row(self):
        return str(self.id), str(self.name), str(self.direction.name)

class Entry:
    def __init__(self, id: int = None, cabinet: Cabinet = None, student: Student = None, laborant: Laborant = None,
                 comp_num: int = None, lesson_num: int = None, lesson_date: date = None):
        self.id = id
        self.student = student
        self.cabinet = cabinet
        self.laborant = laborant
        self.lesson_date = lesson_date
        self.lesson_num = lesson_num
        self.comp_num = comp_num

    def __iter__(self):
        yield 'id', self.id
        yield 'student_id', self.student.id
        yield 'cabinet_id', self.cabinet.id
        yield 'laborant_id', self.laborant.id
        yield 'lesson_date', self.lesson_date
        yield 'lesson_num', self.lesson_num
        yield 'comp_num', self.comp_num

    def to_row(self):
        return (str(self.id), str(self.lesson_num), str(self.comp_num), str(self.student.name), str(self.lesson_date),
                str(self.laborant.second_name), str(self.cabinet.name))

class Page:
    def __init__(self, content: list, page: int, total_pages: int, size: int):
        self.size = size
        self.total_pages = total_pages
        self.page = page
        self.content = content

license: unknown


code:
SQLAlchemy==2.0.23
psycopg2-binary==2.9.9
kivymd @ git+https://github.com/kivymd/KivyMD.git@1152d3c
Kivy==2.2.1


code:
#
# AuroraStore as privileged app as part of a build
#
# Prebuilt content taken from https://gitlab.com/AuroraOSS/AuroraStore/tags
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := privapp-permissions-AuroraServices.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := AuroraServices
LOCAL_SRC_FILES := AuroraServices-v1.0.6.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_REQUIRED_MODULES := privapp-permissions-AuroraServices.xml
include $(BUILD_PREBUILT)


code:
<?xml version="1.0" encoding="utf-8"?>
<permissions>
    <privapp-permissions package="com.aurora.services">
        <permission name="android.permission.INSTALL_PACKAGES"/>
        <permission name="android.permission.DELETE_PACKAGES"/>
    </privapp-permissions>
</permissions>


code:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := privapp-permissions-com.android.vending.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := FakeStore
LOCAL_SRC_FILES := com.android.vending-22.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_REQUIRED_MODULES := privapp-permissions-com.android.vending.xml
include $(BUILD_PREBUILT)


code:
<?xml version="1.0" encoding="utf-8"?>
<permissions>
    <privapp-permissions package="com.android.vending">
        <permission name="android.permission.FAKE_PACKAGE_SIGNATURE"/>
    </privapp-permissions>
</permissions>


code:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := privapp-permissions-com.google.android.gms.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := GmsCore
LOCAL_SRC_FILES := com.google.android.gms-204215028.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_PRIVILEGED_MODULE := true
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_OVERRIDES_PACKAGES := com.qualcomm.location
LOCAL_REQUIRED_MODULES := privapp-permissions-com.google.android.gms.xml
include $(BUILD_PREBUILT)


code:
<?xml version="1.0" encoding="utf-8"?>
<permissions>
    <privapp-permissions package="com.google.android.gms">
        <permission name="android.permission.FAKE_PACKAGE_SIGNATURE"/>
        <permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
        <permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"/>
        <permission name="android.permission.UPDATE_APP_OPS_STATS"/>
</privapp-permissions>
</permissions>


code:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := GsfProxy
LOCAL_SRC_FILES := com.google.android.gsf-8.apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
include $(BUILD_PREBUILT)


code: A subset of packages from https://github.com/lineageos4microg/android_prebuilts_prebuiltapks


code:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := com.google.android.maps.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := com.google.android.maps.xml
include $(BUILD_PREBUILT)

include $(CLEAR_VARS)
LOCAL_MODULE := com.google.android.maps.jar
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := com.google.android.maps.jar
LOCAL_PACKAGE_NAME := com.google.android.maps.jar
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_REQUIRED_MODULES := com.google.android.maps.xml
include $(BUILD_PREBUILT)


code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<permissions>
    <library name="com.google.android.maps"
            file="/system/framework/com.google.android.maps.jar" />
</permissions>


code: mods_disabled/ export-ignore

repo_name: korkut/MeseCraft

path: .gitattributes

license: mit


code:
MeseCraft
==========================
Copyright (C) 2019-2022 MeseCraft, www.mesecraft.com
Thank you to everyone who has contributed to this project, mods, and the MeseCraft and FreeGamers community.
Special thanks to the Minetest community as well. This game was built using the Minetest game engine, minetest_game, and the mods of many others as a foundation.

Description
-------------------------
The best game for Minetest. A survival game with new depth, mechanics, biomes, mobs and many essential additions. Has a focus on being fun, user-friendly, stable, and minimalist but featured. Join our cause: https://www.mesecraft.com

Currently, this game is quite stable and has been debugged on an online server environment with many players. There is active development so new bugs may come and go.

Minetest Engine Compatability
--------------------------
Minetest engine version 5.2.0 or greater is currently recommended to run this game.
Minetest engine version 5.1.1 is compatabile but not all mods and function completely.

License
--------------------------
GNU AFFERO GENERAL PUBLIC LICENSE V3


code:
Glasslands
Coniferous Forest
Deciduous Forest
Rainforest
Savanna
Desert
Sandstone Desert
Cold Desert
Snowy Grassland
Taiga
Tundra
Ice Sheet

Grassland Dunes
Coniferous Forest Dunes
Deciduous Forest Shore
Rainforest Swamp
Savanna Shore
Taiga beach
Tundra Beach

Tundra Ocean
Other Oceans

Undergroud Biome

pyramids


code:
DIMENSION			ELEVATION RANGE			SIZE
Otherworld – Red Asteroids	22001-30912			8000
planet_mars			11000-22000			11000
Otherworld – Asteroids		3500-10999			7500
planet_moon			3000-3300			300
earth orbital space		1000-2999			2000
Cloudlands			200-???
SURFACE
Geomoria			(140)-(170)			30
Railcorridors			(180)-(9500)			9500
DFCaverns – Level1		(193)-(832)			639
DFCaverns – Level2		(832)-(1472)			640
DFCaverns – Level3		(1472)-(2122)			640
DFCaverns – Sunless Sea		(2112)-(2512)			400
DFCaverns – Oil Sea		(2700)				188
DFCaverns – Lava Sea		(2900)				200
DFCaverns – Underworld		(3200)				300
DFCaverns – Primodial		(3393)-(4032)			639
Nether				(16500)-(30000)			14500
Bedrock				(30000)-(30192)			1000


code:
###############
## MESECRAFT ##
## CHANGELOG ##
###############

2022-06-30
* add disabled resources folder /mods_disabled
* documentation updated.
* added new texturing palette file.

2022-02-11
* fixed mobs and items from gliding by removing "builtin_item" mod

2022-01-21
* disabled windspeed affecting player speed
* removed screenshots in folder structure
* compressed nether music to 80kbps

2022-01-20
* fancyvend quick patch

2022-01-20
* remove ethereal, can be added in incrementally but dev focus on less biomes is helpful.
* dfcaverns closer to surface. mobs will need to be adjusted too.

2022-01-19
* rename fireplace
* remove hedges

2022-01-18
* Set annoucement frequency to 20 minutes (1200 seconds).
* Water is now opaque, enforced in game settings.

2022-01-17
* add announcement system to game files.

2022-01-16
* remove walrus
* add weather system

2022-01-12
* urgent fixes for afk update.
* removed main menu background graphic.

2022-01-11
* afk players no longer count for bed.
* added music back to the game.

2022-01-01
* icy slimes fixed to not drop snow on walk.
* adjusted hyperlinks in readme to new site.

2021-07-05
* Removed ts_furniture and ts_workshop to encourage player creativity instead of providing pre-defined furniture nodes.

2021-07-04
* Fixed duplication bug with bee's reported by Mineman. Disabled Bee hive damage.
* Added todo notes for bee's.
* Temporarily disabled music_modpack.

2021-06-30
* Added menu background, header, footer.
* Added parties mod.
* Updated Nether mod.
* Disabled some Ethereal biomes (banana, scortched).

2021-06-29
* Created custom bed mod. Removed Fancy Beds and added colored beds. Also integrated features from "bouncy_bed".
* Added alias for signs

2021-06-28
* Added digiterms, to allow terminals and keyboard input methods to be used with digilines and to encourage Lua education.
* Added some of display_modpack to enable digiterms and display system.
* Removed smartshop.
* Added Fancy Vend for vending that supports pipeworks and digilines.
* Added awards mod to add achievements system.
* Updated minetest_game to release version 5.3.0


code:
README = MeseCraft uses a standardized selection of 64-colors to reduce the complexity of game texturing. Please adhere to the palette colors when making textures. This palette was produced by a color and texturing wizard, so it should be very helpful. A GIMP palette file is included in this directory. There are additional palette files for other applications available at the URL below.
TITLE = ST 64 NATURAL PALETTE
AUTHOR = Skiller Thomson
COLORS = 64
DESCRIPTION = Finally, I did it. My palette collection will be completed, but don't worry, there will be a new collection and I hope you enjoy it. Don't forget to leave the hashtag on social networks.
URL = https://lospec.com/palette-list/st-64-natural


code:
;paint.net Palette File
;Downloaded from Lospec.com/palette-list
;Palette Name: ST 64 Natural
;Description: Finally, I did it. My palette collection will be completed, but don't worry, there will be a new collection and I hope you enjoy it. Don't forget to leave the hashtag on social networks.
;Colors: 64
FF101114
FF1d2247
FF313199
FF543fe0
FF8463e0
FFb896eb
FFd9baf5
FFf3e3e3
FFfcfff2
FFf5d7f3
FFf5c4f2
FFe48deb
FFe063d8
FFb842a0
FF8f3370
FF662548
FF371c37
FF5c1a33
FF991f2f
FFe53737
FFf56d58
FFf59f7f
FFf5ccb0
FFfae7d2
FFf5db93
FFf5be6c
FFeb9b54
FFcc7041
FF8f4a39
FF52321d
FF33211b
FF855d30
FFb88c33
FFe0c03f
FFebdf42
FFecf56c
FFf7fac8
FFcbf558
FF45e02d
FF2cb82c
FF227a2e
FF1d522b
FF16272c
FF338f49
FF42b86d
FF51e099
FF7ff5ca
FFbaf5ef
FF7ff1f5
FF42ceeb
FF258cb8
FF28628f
FF1d3c66
FF33408f
FF496ccc
FF5897f5
FF7fbef5
FFd7e0d9
FFbfc7c1
FFa6ada9
FF7f8582
FF585c5b
FF3b3d3d
FF272829


code:
## Just some notes on how to properly use git.

# add changes
git add .

# commit changes to branch
git commit

# push changes to repo
git push

# Switch working branch in git:
git checkout <branch-name>

# merge changes from "testing" branch into current working branch.
git merge testing

# updates local files from git
git pull


code:
In Linux, in a terminal console, sometimes we need to search for a string of text to find all instances of that text string. We can use grep for this.

grep -r "bucket"

The above command will recursively search for all instances of "bucket" .


code:
Install into Blender:
* io_scene_b3d.zip in Blender to import/export models.
* Edit --> Prefernces --> Addons --> Install
* Only export in object mode.

Steps to modelings:
1. Shape in edit mode and assign vertex groups.
2. UV editing, unwrapping.
3. Make the texture. (Gimp, import color pallette)
4. Make the armature (skeleton/bones).
5. Animate the armature.

Bones should be named same as the vertex groups.


code:
From: https://github.com/minetest/minetest/tree/master

# install dependencies
sudo apt install g++ make libc6-dev cmake libpng-dev libjpeg-dev libxi-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libgmp-dev libjsoncpp-dev libzstd-dev libluajit-5.1-dev

# git minetest
git clone --depth 1 https://github.com/minetest/minetest.git

# git irrlicht-mt build
git clone --depth 1 https://github.com/minetest/irrlicht.git lib/irrlichtmt

# compile minetest
sudo cmake . -DRUN_IN_PLACE=TRUE -DBUILD_SERVER=TRUE -DBUILD_CLIENT=FALSE

# then build the binary
sudo make install

# git MeseCraft and set game in minetest.conf


code:
#!/bin/bash

# Colors with 0 alpha need to be preserved, because opaque leaves ignore alpha.
# For that purpose, the use of indexed colors is disabled (-nc).

find -name '../../../*.png' -print0 | xargs -0 optipng -o7 -zm1-9 -nc -strip all -clobber


code:
*** Reduce particles on Void chest and nether portal
*** Disable knockback mods.

// WEB
* Put MeseCraft on ContentDB (create submodules that target MTG)
* DDNS with HTTPS redirect to https://mesecraft.net --> mesecraft.com

// META
* Adjust Spawn Rates
* Split mobs_creatures and create dfcaverns mobs pack.
* Add more burnable recipes.
* Why is there a steel bar and a steel rod.
* Additional drawer ore upgrades
* Mesecons support for additional doors.
* New protection system, perhaps use cost/rent system.
* Improve the dog following code.
* Change apples, blueberry respawn rate.
* Diet mod support for other foods.
* Make moreblocks different recipes for tar.
* Add poop to more animals.
* Check damage friendly mobs in water
* Add mooshroom cow code for milk from cow
* Mob drops, balance.
* Live stock breeding balance.
* Refill canteens with water bucket, maybe use a water purifcation system, filter straws, unpurified water can make you sick?

// GAME
* add message about send screenshots in to contact@mesecraft.com
* Transparent leaves again
* Ethereal abms for torches, water, ice, etc.
* Radio item that grants a private chat channel?
* Cell phone item for documentation, chat channels, or apps, and commodity market delivery in mailbox after delay.
* Laptop mod check. (Look at lwcomputers)(Centeria)
* Transformation with magic potions into different creatures.
* Add species specific paitings to villages.
* Add painter NPC and paintings.
* Add coins. NPC's can trade with this coin system.
* Shift + Clickdoes not work on lootchests
* Cosmetic slots for accessories.
* 3d armor on death: armor peices have a chance of being completely destroyed.
* Eastern trader that maybe has special eastern items / extra papyrus items.
* Poison api system and override hbhunger.
* Laxative potion item to make the player poop. For fertilizer, but makes you hungerier.
* Implement invasion events: goblins, aliens, nether, pirates, etc.
* Terraria-like pet spawners (e.g. dog whistle)
* Mining helmet spawns a light above while wearing but is weak protection.
* Leather more uses for leather: armor, canteen.
* Rain ABMS can grow crops grass wash out footprints
* Add weeds that can appear in gardens (nettles?)
* Some sort of martian sickness that can infect / sick players or crops.
* If zombie kills you, spawn a similiar looking zombie by bones. or a zomb virus effect.
* Mobs_mc add squid, use horse armor.
* Ignore and party features in chat.
* Goblins, dwarves, elves and items.
* Maybe use moresnow and integrate and bugfix
* Easter event.
* Helicopter integration, vehicles?
* Mobs, undead pirates in shipwrecks.
* Bee biomes underground maybe.
* Halloween event add: improve vampire soudns, string light drops, spider spawn rate increase, black cat, bat increase, cobwebs and spiders.
* Primordial biome ideas: archelogist npc sells fossils and machines. Dinosaur mobs in biome.
* Copy goblins and make a dwarven version that can trade and mine.
* Elves: magical forest biome? Fireflies villages home? roaming in nature in small groups?
* Mars: Giant sandworm from nssm,
* Moon or different planet: facehuggers kill players add a xenoalien.
* Tome of light have weilded light.
* Custom skins for mobs during holidays?
* Check out the rotator screwdriver (Centeria)?
* Snowball gun
* Make certain mobs avoid scarecrows?
* Meseportals for admins, telemosiac for player?
* Pufferfish for kiopy?
* Evil tech company called SynTek
* Add mesecons support to holidaylights
* Independence day celebration: Famous books and quotes by fathers, active around 4th of july and bastille day, fireworks be purchased, summer foods burgers, hotdogs, bbq, red white blue popsicles.
* Punch water to drink, not just sit in water.
* MeseBall (or perhaps trap) to have chance capture monsters that are low health
* Optimize ABMs, global step?
*

// BUGS
* Gramophone top part disappears when picked up. (Centeria fix)
* Fridges aren't protected (remove these from game?)
* Ovens compatible with other food items.
* Spider webs on v5.5  (Centruria Fix)
* Fire-imps do not fly in lava, nor float.
* Add steel sounds to ingot stacks when walking on them.
* Drinks mod makes eating noise, not drinking.
* Fix duplicate portal bug.
* Look at centeria bedrock implentation.
* Slade block, puzzle seal.
* Staff of Earth for nether.
* Add support for other mobs for death messages.
* Fix non-seamless lava sound loop.
* CONTENT_IGNORE when replacing air for giant cave spiders
* farming and ethereal: peppers, ginger, garlic, pineapples, oranges, and strawberries not in game anymore.
* afk mod: if a dead player stays dead long enough to go afk, instead of zzzzz's dummy particles spawn.
* drawers: can't take things out anymore. Numbers decrease while attempting. but not showing in inventory.


code:
Setting Global Git Username and Email

git config --global user.name "Your Name"
git config --global user.email "youremail@yourdomain.com"

Setting Single Repository Username and Email

git config user.name "Your Name"
git config user.email "youremail@yourdomain.com"

Verify that the changes were made correctly:
git config --list


code:
title = MeseCraft
description = The best game for Minetest. A project with the goal of enhancing survival with new depth, features, biomes, mobs and many essential additions. Has a focus on being user-friendly, stable, and fun. Join our cause: https://www.mesecraft.com
author = MeseCraft


code:
# This is a game specific minetest.conf file, do not edit.

################
# Custom lines #
################
opaque_water=1
enable_tnt=true
bones_mode=bones
share_bones_time=1
bones_position_message=false
enable_fire=true
flame_sound=true
give_initial_stuff=true
initial_stuff=default:pick_wood,default:axe_wood,default:sword_wood,default:torch
enable_weather=false

#################
#  MOD CONFIGS  #
#################
# craftguide
craftguide_sfinv_only=true

# climate_api
climate_api_raycast = false
regional_weather_player_speed = false

# commoditymarket_fantasy
commoditymarket_enable_caravan_market=false
commoditymarket_coins_per_ingot=100

# death compass
death_compass_duration=1800

# dfcaverns
dfcaverns_plant_growth_time=3600
dfcaverns_level1_min=-832
dfcaverns_level2_min=-1472
dfcaverns_level3_min=-2112
dfcaverns_sunless_sea_min=-2512
dfcaverns_oil_sea_level=-2700
dfcaverns_lava_sea_level=-2900
dfcaverns_underworld_level=-3200
dfcaverns_primordial_max=-3393
dfcaverns_primordial_min=-4032

# farming_redo
farming_stage_length=6000.0

# font_api
default_font=mozart

# hbarmor
hbarmor_tick=1.0

# hudbars
hudbars_autohide_stamina=false
hudbars_autohide_breath=false
hudbars_tick=1.0
hudbars_bar_type=progress_bar

# item_drop
item_drop.enable_item_drop=true
item_drop.pickup_radius=1.3

# nether
nether_depth_ymax=-17000
nether_depth_ymin=-31000

# mobs_redo
mobs_spawn_protected=false

# music_modpack
music_time_interval=1200
music_display_playback_messages=false
music_default_surface_ymax=1000
music_default_surface_ymin=-100
music_dfcaverns_underground1_ymax=-101
music_dfcaverns_underground1_ymin=-832
music_dfcaverns_underground2_ymax=-833
music_dfcaverns_underground2_ymin=-1472
music_dfcaverns_underground3_ymax=-1473
music_dfcaverns_underground3_ymin=-16999
music_nether_ymax=-17000
music_nether_ymin=-31000

# protector_redo
protector_flip=true

# planet_mars
planet_mars.y_start=11000
planet_mars.y_height=10000
planet_mars.y_skybox_height=10300
planet_mars.y_skybox_height=11000

# planet_moon
planet_moon.miny=3000
planet_moon.maxy=3300
planet_moon.maxsolidy=3200

# radiant_damage
radiant_damage_enable_heat_damage= true
radiant_damage_lava_damage= 2
radiant_damage_fire_damage= 1
radiant_damage_enable_mese_damage=true
radiant_damage_mese_interval=5
radiant_damage_mese_damage=1

# Ropes, disable the extending ladders that come with this mod.
ropes_extending_ladder_enabled=false

# settlements
settlements_show_in_hud=false


code:
MeseCraft Game mod: binoculars
=============================
See license.txt for license information.

Authors of media (textures)
---------------------------
Komodo (CC BY-SA 3.0):
mesecraft_binoculars.png

Usage
-----
In survival mode, use of zoom requires the binoculars item in your inventory,
they will allow a 10 degree field of view.

It can take up to 5 seconds for adding to or removal from inventory to have an
effect, however to instantly allow the use of this zoom 'use' (leftclick) the
item.

Zoom with a field of view of 15 degrees is automatically allowed in creative
mode and for any player with the 'creative' privilege.

The 'binoculars.update_player_property()' function is global so can be
redefined by a mod for alternative behaviour.


code:
-- MeseCraft Game module: mesecraft_binoculars
-- This version is forked from the Minetest Game mod "binoculars".
-- See README.txt for licensing and other information.

mesecraft_binoculars = {}

-- Load support for MT game translation.
local S = minetest.get_translator("mesecraft_binoculars")

-- Detect creative mod
local creative_mod = minetest.get_modpath("creative")

-- Cache creative mode setting as fallback if creative mod not present
local creative_mode_cache = minetest.settings:get_bool("creative_mode")

-- Update player property
-- Global to allow overriding

function mesecraft_binoculars.update_player_property(player)
	local creative_enabled =
		(creative_mod and creative.is_enabled_for(player:get_player_name())) or
		creative_mode_cache
	local new_zoom_fov = 0

	if player:get_inventory():contains_item(
			"main", "mesecraft_binoculars:binoculars") then
		new_zoom_fov = 10
	elseif creative_enabled then
		new_zoom_fov = 15
	end

	-- Only set property if necessary to avoid player mesh reload
	if player:get_properties().zoom_fov ~= new_zoom_fov then
		player:set_properties({zoom_fov = new_zoom_fov})
	end
end

-- Set player property 'on joinplayer'

minetest.register_on_joinplayer(function(player)
	mesecraft_binoculars.update_player_property(player)
end)

-- Cyclic update of player property

local function cyclic_update()
	for _, player in ipairs(minetest.get_connected_players()) do
		mesecraft_binoculars.update_player_property(player)
	end
	minetest.after(4.7, cyclic_update)
end

minetest.after(4.7, cyclic_update)

-- Binoculars item

minetest.register_craftitem("mesecraft_binoculars:binoculars", {
	description = S("Binoculars") .. "\n" .. S("Use with 'Zoom' key"),
	inventory_image = "mesecraft_binoculars.png",
	stack_max = 1,

	on_use = function(itemstack, user, pointed_thing)
		mesecraft_binoculars.update_player_property(user)
	end,
})

-- Crafting

minetest.register_craft({
	output = "mesecraft_binoculars:binoculars",
	recipe = {
		{"default:obsidian_glass", "", "default:obsidian_glass"},
		{"default:bronze_ingot", "default:bronze_ingot", "default:bronze_ingot"},
		{"default:obsidian_glass", "", "default:obsidian_glass"},
	}
})


code:
# textdomain: binoculars
Binoculars=Fernglas
Use with 'Zoom' key=Mit „Zoom“-Taste benutzen


code:
# textdomain: binoculars
Binoculars=Prismáticos
Use with 'Zoom' key=Usar con la tecla 'Zoom'


code:
# textdomain: binoculars
Binoculars=Jumelles
Use with 'Zoom' key=Utiliser avec le bouton « Zoom »


code:
# textdomain: binoculars
Binoculars=Binokular
Use with 'Zoom' key=Pakai dengan tombol 'Zum'


code:
# textdomain: binoculars
Binoculars=Binocolo
Use with 'Zoom' key=Usalo col tasto 'Ingrandimento'


code:
# textdomain: binoculars
Binoculars=Binokular
Use with 'Zoom' key=Guna dengan kekunci 'Zum'


code:
# textdomain: binoculars
Binoculars=Бинокль
Use with 'Zoom' key=Используется с привилегией 'Zoom'


code:
# textdomain: binoculars
Binoculars=Kikare
Use with 'Zoom' key=Används med 'Zoom' knappen


code:
# textdomain: binoculars
Binoculars=望远镜
Use with 'Zoom' key=与“缩放”键一起使用


code:
# textdomain: binoculars
Binoculars=望遠鏡
Use with 'Zoom' key=與“縮放”鍵一起使用


code:
# textdomain: binoculars
Binoculars=
Use with 'Zoom' key=


code:
name = mesecraft_binoculars
description = MeseCraft Game mod: binoculars
depends = default
optional_depends = creative


code:
MeseCraft Game mod: mesecraft_bones
forked from Minetest Game mod: bones
========================
See license.txt for license information.

Authors of source code
----------------------
Originally by PilzAdam (MIT)
Various Minetest developers and contributors (MIT)
MeseCraft

Authors of media (textures)
---------------------------
Former textures: paramat (CC BY-SA 3.0)
MeseCraft textures: Komodo (CC BY-SA 3.0)


code:
-- mesecraft_bones/init.lua
-- See README.txt for licensing and other information.
-- Load support for MeseCraft translation.

local S = minetest.get_translator("mesecraft_bones")

mesecraft_bones = {}

local function is_owner(pos, name)
	local owner = minetest.get_meta(pos):get_string("owner")
	if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
		return true
	end
	return false
end

local bones_formspec =
	"size[8,9]" ..
	"list[current_name;main;0,0.3;8,4;]" ..
	"list[current_player;main;0,4.85;8,1;]" ..
	"list[current_player;main;0,6.08;8,3;8]" ..
	"listring[current_name;main]" ..
	"listring[current_player;main]" ..
	default.get_hotbar_bg(0,4.85)

local share_bones_time = tonumber(minetest.settings:get("share_bones_time")) or 1200
local share_bones_time_early = tonumber(minetest.settings:get("share_bones_time_early")) or share_bones_time / 4

minetest.register_node("mesecraft_bones:bones", {
	description = S("Bones"),
	tiles = {
		"mesecraft_bones_top.png^[transform2",
		"mesecraft_bones_bottom.png",
		"mesecraft_bones_side.png",
		"mesecraft_bones_side.png",
		"mesecraft_bones_rear.png",
		"mesecraft_bones_front.png"
	},
	paramtype2 = "facedir",
	groups = {dig_immediate = 2},
	sounds = default.node_sound_gravel_defaults(),

	can_dig = function(pos, player)
		local inv = minetest.get_meta(pos):get_inventory()
		local name = ""
		if player then
			name = player:get_player_name()
		end
		return is_owner(pos, name) and inv:is_empty("main")
	end,

	allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
		if is_owner(pos, player:get_player_name()) then
			return count
		end
		return 0
	end,

	allow_metadata_inventory_put = function(pos, listname, index, stack, player)
		return 0
	end,

	allow_metadata_inventory_take = function(pos, listname, index, stack, player)
		if is_owner(pos, player:get_player_name()) then
			return stack:get_count()
		end
		return 0
	end,

	on_metadata_inventory_take = function(pos, listname, index, stack, player)
		local meta = minetest.get_meta(pos)
		if meta:get_inventory():is_empty("main") then
			local inv = player:get_inventory()
			if inv:room_for_item("main", {name = "mesecraft_bones:bones"}) then
				inv:add_item("main", {name = "mesecraft_bones:bones"})
			else
				minetest.add_item(pos, "mesecraft_bones:bones")
			end
			minetest.remove_node(pos)
		end
	end,

	on_punch = function(pos, node, player)
		if not is_owner(pos, player:get_player_name()) then
			return
		end

		if minetest.get_meta(pos):get_string("infotext") == "" then
			return
		end

		local inv = minetest.get_meta(pos):get_inventory()
		local player_inv = player:get_inventory()
		local has_space = true

		for i = 1, inv:get_size("main") do
			local stk = inv:get_stack("main", i)
			if player_inv:room_for_item("main", stk) then
				inv:set_stack("main", i, nil)
				player_inv:add_item("main", stk)
			else
				has_space = false
				break
			end
		end

		-- remove bones if player emptied them
		if has_space then
			if player_inv:room_for_item("main", {name = "mesecraft_bones:bones"}) then
				player_inv:add_item("main", {name = "mesecraft_bones:bones"})
			else
				minetest.add_item(pos,"mesecraft_bones:bones")
			end
			minetest.remove_node(pos)
		end
	end,

	on_timer = function(pos, elapsed)
		local meta = minetest.get_meta(pos)
		local time = meta:get_int("time") + elapsed
		if time >= share_bones_time then
			meta:set_string("infotext", S("@1's old bones", meta:get_string("owner")))
			meta:set_string("owner", "")
		else
			meta:set_int("time", time)
			return true
		end
	end,
	on_blast = function(pos)
	end,
})

local function may_replace(pos, player)
	local node_name = minetest.get_node(pos).name
	local node_definition = minetest.registered_nodes[node_name]

	-- if the node is unknown, we return false
	if not node_definition then
		return false
	end

	-- allow replacing air and liquids
	if node_name == "air" or node_definition.liquidtype ~= "none" then
		return true
	end

	-- don't replace filled chests and other nodes that don't allow it
	local can_dig_func = node_definition.can_dig
	if can_dig_func and not can_dig_func(pos, player) then
		return false
	end

	-- default to each nodes buildable_to; if a placed block would replace it, why shouldn't bones?
	-- flowers being squished by bones are more realistical than a squished stone, too
	-- exception are of course any protected buildable_to
	return node_definition.buildable_to and not minetest.is_protected(pos, player:get_player_name())
end

local drop = function(pos, itemstack)
	local obj = minetest.add_item(pos, itemstack:take_item(itemstack:get_count()))
	if obj then
		obj:set_velocity({
			x = math.random(-10, 10) / 9,
			y = 5,
			z = math.random(-10, 10) / 9,
		})
	end
end

local player_inventory_lists = { "main", "craft" }
mesecraft_bones.player_inventory_lists = player_inventory_lists

local function is_all_empty(player_inv)
	for _, list_name in ipairs(player_inventory_lists) do
		if not player_inv:is_empty(list_name) then
			return false
		end
	end
	return true
end

minetest.register_on_dieplayer(function(player)

	local bones_mode = minetest.settings:get("bones_mode") or "bones"
	if bones_mode ~= "bones" and bones_mode ~= "drop" and bones_mode ~= "keep" then
		bones_mode = "bones"
	end

	local bones_position_message = minetest.settings:get_bool("bones_position_message") == true
	local player_name = player:get_player_name()
	local pos = vector.round(player:get_pos())
	local pos_string = minetest.pos_to_string(pos)

	-- return if keep inventory set or in creative mode
	if bones_mode == "keep" or (creative and creative.is_enabled_for
			and creative.is_enabled_for(player:get_player_name())) then
		minetest.log("action", player_name .. " dies at " .. pos_string ..
			". No bones placed")
		if bones_position_message then
			minetest.chat_send_player(player_name, S("@1 died at @2.", player_name, pos_string))
		end
		return
	end

	local player_inv = player:get_inventory()
	if is_all_empty(player_inv) then
		minetest.log("action", player_name .. " dies at " .. pos_string ..
			". No bones placed")
		if bones_position_message then
			minetest.chat_send_player(player_name, S("@1 died at @2.", player_name, pos_string))
		end
		return
	end

	-- check if it's possible to place bones, if not find space near player
	if bones_mode == "bones" and not may_replace(pos, player) then
		local air = minetest.find_node_near(pos, 1, {"air"})
		if air and not minetest.is_protected(air, player_name) then
			pos = air
		else
			bones_mode = "drop"
		end
	end

	if bones_mode == "drop" then
		for _, list_name in ipairs(player_inventory_lists) do
			for i = 1, player_inv:get_size(list_name) do
				drop(pos, player_inv:get_stack(list_name, i))
			end
			player_inv:set_list(list_name, {})
		end
		drop(pos, ItemStack("mesecraft_bones:bones"))
		minetest.log("action", player_name .. " dies at " .. pos_string ..
			". Inventory dropped")
		if bones_position_message then
			minetest.chat_send_player(player_name, S("@1 died at @2, and dropped their inventory.", player_name, pos_string))
		end
		return
	end

	local param2 = minetest.dir_to_facedir(player:get_look_dir())
	minetest.set_node(pos, {name = "mesecraft_bones:bones", param2 = param2})

	minetest.log("action", player_name .. " dies at " .. pos_string ..
		". Bones placed")
	if bones_position_message then
		minetest.chat_send_player(player_name, S("@1 died at @2, and bones were placed.", player_name, pos_string))
	end

	local meta = minetest.get_meta(pos)
	local inv = meta:get_inventory()
	inv:set_size("main", 8 * 4)

	for _, list_name in ipairs(player_inventory_lists) do
		for i = 1, player_inv:get_size(list_name) do
			local stack = player_inv:get_stack(list_name, i)
			if inv:room_for_item("main", stack) then
				inv:add_item("main", stack)
			else -- no space left
				drop(pos, stack)
			end
		end
		player_inv:set_list(list_name, {})
	end

	meta:set_string("formspec", bones_formspec)
	meta:set_string("owner", player_name)

	if share_bones_time ~= 0 then
		meta:set_string("infotext", S("@1's fresh bones", player_name))

		if share_bones_time_early == 0 or not minetest.is_protected(pos, player_name) then
			meta:set_int("time", 0)
		else
			meta:set_int("time", (share_bones_time - share_bones_time_early))
		end

		minetest.get_node_timer(pos):start(10)
	else
		meta:set_string("infotext", S("@1's bones", player_name))
	end
end)


code:
# textdomain: bones
Bones=Knochen
@1's old bones=Alte Knochen von @1
@1 died at @2.=@1 starb bei @2.
@1 died at @2, and dropped their inventory.=@1 starb bei @2 und ließ das Inventar fallen.
@1 died at @2, and bones were placed.=@1 starb bei @2 und Knochen wurden platziert.
@1's fresh bones=Frische Knochen von @1
@1's bones=Knochen von @1


code:
# textdomain: bones
Bones=Huesos
@1's old bones=Huesos antiguos de @1
@1 died at @2.=@1 murió en @2.
@1 died at @2, and dropped their inventory.=@1 murió en @2, y su inventario se desprendió.
@1 died at @2, and bones were placed.=@1 murió en @2, y sus huesos fueron depositados.
@1's fresh bones=Huesos recientes de @1
@1's bones=Huesos de @1


code:
# textdomain: bones
Bones=Os
@1's old bones=Vieux os de @1
@1 died at @2.=@1 est mort à @2.
@1 died at @2, and dropped their inventory.=@1 est mort à @2 et a laissé tomber son inventaire.
@1 died at @2, and bones were placed.=@1 est mort à @2 et ses os ont été placés.
@1's fresh bones=Os frais de @1
@1's bones=Os de @1


code:
# textdomain: bones
Bones=Tulang
@1's old bones=Tulang lama @1
@1 died at @2.=@1 mati di @2.
@1 died at @2, and dropped their inventory.=@1 mati di @2 dan meninggalkan barangnya.
@1 died at @2, and bones were placed.=@1 mati di @2 dan tulangnya diletakkan.
@1's fresh bones=Tulang segar @1
@1's bones=Tulang @1


code:
# textdomain: bones
Bones=Ossa
@1's old bones=Ossa vecchie di @1
@1 died at @2.=@1 è morto alla posizione @2.
@1 died at @2, and dropped their inventory.=@1 è morto alla posizione @2, e ha lasciato a terra il contenuto del suo inventario.
@1 died at @2, and bones were placed.=@1 è morto alla posizione @2, e vi sono state posizionate delle ossa.
@1's fresh bones=Ossa fresche di @1
@1's bones=Ossa di @1


code:
# textdomain: bones
Bones=Tulang
@1's old bones=Tulang lama @1
@1 died at @2.=@1 mati di @2.
@1 died at @2, and dropped their inventory.=@1 mati di @2, dan menjatuhkan inventorinya.
@1 died at @2, and bones were placed.=@1 mati di @2, dan tulang diletakkan.
@1's fresh bones=Tulang segar @1
@1's bones=Tulang @1


code:
# textdomain: bones
Bones=Кости
@1's old bones=Старые кости @1
@1 died at @2.=@1 умер в @2.
@1 died at @2, and dropped their inventory.=@1 умер в @2 и потерял содержимое своего инвентаря.
@1 died at @2, and bones were placed.=@1 умер в @2, помещены кости.
@1's fresh bones=новые кости @1
@1's bones=кости @1


code:
# textdomain: bones
Bones=Ben
@1's old bones=@1s Gamla ben
@1 died at @2.=@1 dog på @a.
@1 died at @2, and dropped their inventory.=@1 dog på @a, och tappade deras saker.
@1 died at @2, and bones were placed.=@1 dog på @2, och deras ben var placerade.
@1's fresh bones=@1s färska ben
@1's bones=@1s ben


code:
# textdomain: bones
Bones=骨骸
@1's old bones=@1的旧骨骸
@1 died at @2.=@1在@2死亡。
@1 died at @2, and dropped their inventory.=@1在@2死亡，丢掉了物品栏。
@1 died at @2, and bones were placed.=@1在@2死亡，骨骸被放置。
@1's fresh bones=@1的新鲜骨骸
@1's bones=@1的骨骸


code:
# textdomain: bones
Bones=骨骸
@1's old bones=@1的舊骨骸
@1 died at @2.=@1在@2死亡。
@1 died at @2, and dropped their inventory.=@1在@2死亡，丟掉了物品欄。
@1 died at @2, and bones were placed.=@1在@2死亡，骨骸被放置。
@1's fresh bones=@1的新鮮骨骸
@1's bones=@1的骨骸


code:
# textdomain: bones
Bones=
@1's old bones=
@1 died at @2.=
@1 died at @2, and dropped their inventory.=
@1 died at @2, and bones were placed.=
@1's fresh bones=
@1's bones=


code:
name = mesecraft_bones
description = MeseCraft Game mod: mesecraft_bones
depends = default


code:
Minetest Game mod: bucket
=========================
See license.txt for license information.

Authors of source code
----------------------
Kahrl <kahrl@gmx.net> (LGPLv2.1+)
celeron55, Perttu Ahola <celeron55@gmail.com> (LGPLv2.1+)
Various Minetest developers and contributors (LGPLv2.1+)

Authors of media (textures)
---------------------------
Komodo (CC BY-SA 3.0)


code:
-- MeseCraft Game module: mesecraft_bucket
-- This version is forked from the Minetest Game mod "bucket".
-- See README.txt for licensing and other information.

-- Load support for game translation.
local S = minetest.get_translator("mesecraft_bucket")

-- Register the bucket craft item.
minetest.register_craft({
	output = "mesecraft_bucket:bucket_empty 1",
	recipe = {
		{"default:steel_ingot", "", "default:steel_ingot"},
		{"", "default:steel_ingot", ""},
	}
})

-- Initialize
mesecraft_bucket = {}
mesecraft_bucket.liquids = {}

-- Protection compatibility
local function check_protection(pos, name, text)
	if minetest.is_protected(pos, name) then
		minetest.log("action", (name ~= "" and name or "A mod")
			.. " tried to " .. text
			.. " at protected position "
			.. minetest.pos_to_string(pos)
			.. " with a bucket")
		minetest.record_protection_violation(pos, name)
		return true
	end
	return false
end

-- Register a new liquid
--    source = name of the source node
--    flowing = name of the flowing node
--    itemname = name of the new bucket item (or nil if liquid is not takeable)
--    inventory_image = texture of the new bucket item (ignored if itemname == nil)
--    name = text description of the bucket item
--    groups = (optional) groups of the bucket item, for example {water_bucket = 1}
--    force_renew = (optional) bool. Force the liquid source to renew if it has a
--                  source neighbour, even if defined as 'liquid_renewable = false'.
--                  Needed to avoid creating holes in sloping rivers.
-- This function can be called from any mod (that depends on bucket).
function mesecraft_bucket.register_liquid(source, flowing, itemname, inventory_image, name,
		groups, force_renew)
	mesecraft_bucket.liquids[source] = {
		source = source,
		flowing = flowing,
		itemname = itemname,
		force_renew = force_renew,
	}
	mesecraft_bucket.liquids[flowing] = mesecraft_bucket.liquids[source]

	if itemname ~= nil then
		minetest.register_craftitem(itemname, {
			description = name,
			inventory_image = inventory_image,
			stack_max = 1,
			liquids_pointable = true,
			groups = groups,

			on_place = function(itemstack, user, pointed_thing)
				-- Must be pointing to node
				if pointed_thing.type ~= "node" then
					return
				end

				local node = minetest.get_node_or_nil(pointed_thing.under)
				local ndef = node and minetest.registered_nodes[node.name]

				-- Call on_rightclick if the pointed node defines it
				if ndef and ndef.on_rightclick and
						not (user and user:is_player() and
						user:get_player_control().sneak) then
					return ndef.on_rightclick(
						pointed_thing.under,
						node, user,
						itemstack)
				end

				local lpos

				-- Check if pointing to a buildable node
				if ndef and ndef.buildable_to then
					-- buildable; replace the node
					lpos = pointed_thing.under
				else
					-- not buildable to; place the liquid above
					-- check if the node above can be replaced

					lpos = pointed_thing.above
					node = minetest.get_node_or_nil(lpos)
					local above_ndef = node and minetest.registered_nodes[node.name]

					if not above_ndef or not above_ndef.buildable_to then
						-- do not remove the bucket with the liquid
						return itemstack
					end
				end

				if check_protection(lpos, user
						and user:get_player_name()
						or "", "place "..source) then
					return
				end

				minetest.set_node(lpos, {name = source})
				return ItemStack("mesecraft_bucket:bucket_empty")
			end
		})
	end
end

minetest.register_craftitem("mesecraft_bucket:bucket_empty", {
	description = S("Empty Bucket"),
	inventory_image = "mesecraft_bucket.png",
	groups = {tool = 1},
	liquids_pointable = true,
	on_use = function(itemstack, user, pointed_thing)
		if pointed_thing.type == "object" then
			pointed_thing.ref:punch(user, 1.0, { full_punch_interval=1.0 }, nil)
			return user:get_wielded_item()
		elseif pointed_thing.type ~= "node" then
			-- do nothing if it's neither object nor node
			return
		end
		-- Check if pointing to a liquid source
		local node = minetest.get_node(pointed_thing.under)
		local liquiddef = mesecraft_bucket.liquids[node.name]
		local item_count = user:get_wielded_item():get_count()

		if liquiddef ~= nil
		and liquiddef.itemname ~= nil
		and node.name == liquiddef.source then
			if check_protection(pointed_thing.under,
					user:get_player_name(),
					"take ".. node.name) then
				return
			end

			-- default set to return filled bucket
			local giving_back = liquiddef.itemname

			-- check if holding more than 1 empty bucket
			if item_count > 1 then

				-- if space in inventory add filled bucked, otherwise drop as item
				local inv = user:get_inventory()
				if inv:room_for_item("main", {name=liquiddef.itemname}) then
					inv:add_item("main", liquiddef.itemname)
				else
					local pos = user:get_pos()
					pos.y = math.floor(pos.y + 0.5)
					minetest.add_item(pos, liquiddef.itemname)
				end

				-- set to return empty buckets minus 1
				giving_back = "mesecraft_bucket:bucket_empty "..tostring(item_count-1)

			end

			-- force_renew requires a source neighbour
			local source_neighbor = false
			if liquiddef.force_renew then
				source_neighbor =
					minetest.find_node_near(pointed_thing.under, 1, liquiddef.source)
			end
			if not (source_neighbor and liquiddef.force_renew) then
				minetest.add_node(pointed_thing.under, {name = "air"})
			end

			return ItemStack(giving_back)
		else
			-- non-liquid nodes will have their on_punch triggered
			local node_def = minetest.registered_nodes[node.name]
			if node_def then
				node_def.on_punch(pointed_thing.under, node, user, pointed_thing)
			end
			return user:get_wielded_item()
		end
	end,
})

mesecraft_bucket.register_liquid(
	"default:water_source",
	"default:water_flowing",
	"mesecraft_bucket:bucket_water",
	"mesecraft_bucket_water.png",
	S("Water Bucket"),
	{tool = 1, water_bucket = 1}
)

-- River water source is 'liquid_renewable = false' to avoid horizontal spread
-- of water sources in sloping rivers that can cause water to overflow
-- riverbanks and cause floods.
-- River water source is instead made renewable by the 'force renew' option
-- used here.

mesecraft_bucket.register_liquid(
	"default:river_water_source",
	"default:river_water_flowing",
	"mesecraft_bucket:bucket_river_water",
	"mesecraft_bucket_river_water.png",
	S("River Water Bucket"),
	{tool = 1, water_bucket = 1},
	true
)

mesecraft_bucket.register_liquid(
	"default:lava_source",
	"default:lava_flowing",
	"mesecraft_bucket:bucket_lava",
	"mesecraft_bucket_lava.png",
	S("Lava Bucket"),
	{tool = 1}
)

minetest.register_craft({
	type = "fuel",
	recipe = "mesecraft_bucket:bucket_lava",
	burntime = 60,
	replacements = {{"mesecraft_bucket:bucket_lava", "mesecraft_bucket:bucket_empty"}},
})

-- Register buckets as dungeon loot
if minetest.global_exists("dungeon_loot") then
	dungeon_loot.register({
		{name = "mesecraft_bucket:bucket_empty", chance = 0.55},
		-- water in deserts/ice or above ground, lava otherwise
		{name = "mesecraft_bucket:bucket_water", chance = 0.45,
			types = {"sandstone", "desert", "ice"}},
		{name = "mesecraft_bucket:bucket_water", chance = 0.45, y = {0, 32768},
			types = {"normal"}},
		{name = "mesecraft_bucket:bucket_lava", chance = 0.45, y = {-32768, -1},
			types = {"normal"}},
	})
end


code:
# textdomain: bucket
Empty Bucket=Leerer Eimer
Water Bucket=Wassereimer
River Water Bucket=Flusswassereimer
Lava Bucket=Lavaeimer


code:
# textdomain: bucket
Empty Bucket=Cubo vacío
Water Bucket=Cubo con agua
River Water Bucket=Cubo con agua de río
Lava Bucket=Cubo con lava


code:
# textdomain: bucket
Empty Bucket=Seau vide
Water Bucket=Seau d'eau
River Water Bucket=Seau d'eau de rivière
Lava Bucket=Seau de lave


code:
# textdomain: bucket
Empty Bucket=Ember Kosong
Water Bucket=Ember Air
River Water Bucket=Ember Air Sungai
Lava Bucket=Ember Lava


code:
# textdomain: bucket
Empty Bucket=Secchio vuoto
Water Bucket=Secchio d'acqua
River Water Bucket=Secchio d'acqua di fiume
Lava Bucket=Secchio di lava


code:
# textdomain: bucket
Empty Bucket=Baldi Kosong
Water Bucket=Baldi Air
River Water Bucket=Baldi Air Sungai
Lava Bucket=Baldi Lava


code:
# textdomain: bucket
Empty Bucket=Пустое Ведро
Water Bucket=Ведро с Водой
River Water Bucket=Ведро с Речной Водой
Lava Bucket=Ведро с Лавой


code:
# textdomain: bucket
Empty Bucket=Tom hink
Water Bucket=Vatten hink
River Water Bucket=Flodvatten hink
Lava Bucket=Lava hink


code:
# textdomain: bucket
Empty Bucket=空桶
Water Bucket=水桶
River Water Bucket=河水桶
Lava Bucket=岩浆桶


code:
# textdomain: bucket
Empty Bucket=空桶
Water Bucket=水桶
River Water Bucket=河水桶
Lava Bucket=岩漿桶


code:
# textdomain: bucket
Empty Bucket=
Water Bucket=
River Water Bucket=
Lava Bucket=


code:
name = mesecraft_bucket
description = MeseCraft Game mod: mesecraft_bucket
depends = default
optional_depends = dungeon_loot


code:
Minetest Game mod: Butterflies
==============================
Adds butterflies to the world on mapgen, which can be caught in a net if the
fireflies mod is also enabled.

Authors of source code
----------------------
Shara RedCat (MIT)

Authors of media (textures)
---------------------------
Shara RedCat (CC BY-SA 3.0):
  butterflies_butterfly_*.png
  butterflies_butterfly_*_animated.png


code:
-- MeseCraft game module: butterflies/init.lua
-- This version is forked from the Minetest Game mod "butterflies".
-- See README.txt for licensing and other information.

-- Load support for MeseCraft game translation.
local S = minetest.get_translator("mesecraft_butterflies")

-- register butterflies
local butter_list = {
	{"white",  S("White Butterfly")},
	{"red",    S("Red Butterfly")},
	{"violet", S("Violet Butterfly")}
}

for i in ipairs (butter_list) do
	local name = butter_list[i][1]
	local desc = butter_list[i][2]

	minetest.register_node("mesecraft_butterflies:butterfly_"..name, {
		description = desc,
		drawtype = "plantlike",
		tiles = {{
			name = "mesecraft_butterflies_butterfly_"..name.."_animated.png",
			animation = {
				type = "vertical_frames",
				aspect_w = 16,
				aspect_h = 16,
				length = 3
			},
		}},
		inventory_image = "mesecraft_butterflies_butterfly_"..name..".png",
		wield_image =  "mesecraft_butterflies_butterfly_"..name..".png",
		waving = 1,
		paramtype = "light",
		sunlight_propagates = true,
		buildable_to = true,
		walkable = false,
		groups = {catchable = 1},
		selection_box = {
			type = "fixed",
			fixed = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
		},
		floodable = true,
		on_place = function(itemstack, placer, pointed_thing)
			local player_name = placer:get_player_name()
			local pos = pointed_thing.above

			if not minetest.is_protected(pos, player_name) and
					not minetest.is_protected(pointed_thing.under, player_name) and
					minetest.get_node(pos).name == "air" then
				minetest.set_node(pos, {name = "mesecraft_butterflies:butterfly_"..name})
				minetest.get_node_timer(pos):start(1)
				itemstack:take_item()
			end
			return itemstack
		end,
		on_timer = function(pos, elapsed)
			if minetest.get_node_light(pos) < 11 then
				minetest.set_node(pos, {name = "mesecraft_butterflies:hidden_butterfly_"..name})
			end
			minetest.get_node_timer(pos):start(30)
		end
	})

	minetest.register_node("mesecraft_butterflies:hidden_butterfly_"..name, {
		drawtype = "airlike",
		inventory_image = "mesecraft_butterflies_butterfly_"..name..".png",
		wield_image =  "mesecraft_butterflies_butterfly_"..name..".png",
		paramtype = "light",
		sunlight_propagates = true,
		walkable = false,
		pointable = false,
		diggable = false,
		drop = "",
		groups = {not_in_creative_inventory = 1},
		floodable = true,
		on_place = function(itemstack, placer, pointed_thing)
			local player_name = placer:get_player_name()
			local pos = pointed_thing.above

			if not minetest.is_protected(pos, player_name) and
					not minetest.is_protected(pointed_thing.under, player_name) and
					minetest.get_node(pos).name == "air" then
				minetest.set_node(pos, {name = "mesecraft_butterflies:hidden_butterfly_"..name})
				minetest.get_node_timer(pos):start(1)
				itemstack:take_item()
			end
			return itemstack
		end,
		on_timer = function(pos, elapsed)
			if minetest.get_node_light(pos) >= 11 then
				minetest.set_node(pos, {name = "mesecraft_butterflies:butterfly_"..name})
			end
			minetest.get_node_timer(pos):start(30)
		end
	})
end

-- register decoration
minetest.register_decoration({
	name = "mesecraft_butterflies:butterfly",
	deco_type = "simple",
	place_on = {"default:dirt_with_grass"},
	place_offset_y = 2,
	sidelen = 80,
	fill_ratio = 0.005,
	biomes = {"grassland", "deciduous_forest"},
	y_max = 31000,
	y_min = 1,
	decoration = {
		"mesecraft_butterflies:butterfly_white",
		"mesecraft_butterflies:butterfly_red",
		"mesecraft_butterflies:butterfly_violet"
	},
	spawn_by = "group:flower",
	num_spawn_by = 1
})

-- get decoration ID
local butterflies = minetest.get_decoration_id("mesecraft_butterflies:butterfly")
minetest.set_gen_notify({decoration = true}, {butterflies})

-- start nodetimers
minetest.register_on_generated(function(minp, maxp, blockseed)
	local gennotify = minetest.get_mapgen_object("gennotify")
	local poslist = {}

	for _, pos in ipairs(gennotify["decoration#"..butterflies] or {}) do
		local deco_pos = {x = pos.x, y = pos.y + 3, z = pos.z}
		table.insert(poslist, deco_pos)
	end

	if #poslist ~= 0 then
		for i = 1, #poslist do
			local pos = poslist[i]
			minetest.get_node_timer(pos):start(1)
		end
	end
end)


code:
# textdomain: butterflies
White Butterfly=Weißer Schmetterling
Red Butterfly=Roter Schmetterling
Violet Butterfly=Violetter Schmetterling


code:
# textdomain: butterflies
White Butterfly=Mariposa blanca
Red Butterfly=Mariposa roja
Violet Butterfly=Mariposa violeta


code:
# textdomain: butterflies
White Butterfly=Papillon blanc
Red Butterfly=Papillon rouge
Violet Butterfly=Papillon violet


code:
# textdomain: butterflies
White Butterfly=Kupu-Kupu Putih
Red Butterfly=Kupu-Kupu Merah
Violet Butterfly=Kupu-Kupu Ungu


code:
# textdomain: butterflies
White Butterfly=Farfalla bianca
Red Butterfly=Farfalla rossa
Violet Butterfly=Farfalla viola


code:
# textdomain: butterflies
White Butterfly=Rama-Rama Putih
Red Butterfly=Rama-Rama Merah
Violet Butterfly=Rama-Rama Ungu


code:
# textdomain: butterflies
White Butterfly=Белая Бабочка
Red Butterfly=Красная Бабочка
Violet Butterfly=Фиолетовая Бабочка


code:
# textdomain: butterflies
White Butterfly=Vit fjäril
Red Butterfly=Röd fjäril
Violet Butterfly=Violett fjäril


code:
# textdomain: butterflies
White Butterfly=白蝴蝶
Red Butterfly=红蝴蝶
Violet Butterfly=紫蝴蝶


code:
# textdomain: butterflies
White Butterfly=白蝴蝶
Red Butterfly=紅蝴蝶
Violet Butterfly=紫蝴蝶


code:
# textdomain: butterflies
White Butterfly=
Red Butterfly=
Violet Butterfly=


code:
name = mesecraft_butterflies
description = MeseCraft game module: MeseCraft Butterflies
depends = default, flowers


code:
name = ENTITIES
description = Meta-modpack containing entity-related mods for MeseCraft.


code:
[submodule "climate_api"]
	path = climate_api
	url = https://github.com/t-affeldt/climate_api.git
[submodule "regional_weather"]
	path = regional_weather
	url = https://github.com/t-affeldt/regional_weather.git
[submodule "moon_phases"]
	path = moon_phases
	url = https://github.com/t-affeldt/minetest_moon_phase
[submodule "lightning"]
	path = lightning
	url = https://github.com/minetest-mods/lightning.git
[submodule "sailing_kit"]
	path = sailing_kit
	url = https://github.com/t-affeldt/sailing_kit.git
[submodule "mobkit"]
	path = mobkit
	url = https://github.com/TheTermos/mobkit


code:
# Climate Modpack
Not every biome is the same and neither should their weather be.
The complete weather bundle for any Minetest game.

![](https://raw.githubusercontent.com/t-affeldt/regional_weather/master/screenshot.webp)

Source & Information on [Github](https://github.com/t-affeldt/climate) and the [Forum](https://forum.minetest.net/viewtopic.php?t=24569).

## Included Mods
- Climate API: The heart and core of this pack. This mod provides a vast weather and effect engine
- Regional Weather: What you will see on the screen. A bundle of weather presets and environment effects with beautiful visuals
- Moon Phases: Makes your sky dynamic by cycling through eight different phases
- Lightning by Auke Kok (sofar): Adds random lightning strikes during rainstorms

## Recommended Mods
- [Sailing Kit](https://github.com/t-affeldt/sailing_kit): A fork of [Termos' sailboat](https://forum.minetest.net/viewtopic.php?t=23520) to support the new wind system.

## Cloning Instructions
This modpack uses submodules to always be up-to-date.
Downloading the repository as a ZIP file leaves the mod folders empty, so you will have to download them manually. If you are using git commands then make sure you set the *recursive* flag instead: ``git clone https://github.com/t-affeldt/climate.git --recursive``. If you forget to set this flag, then the mod folders will be empty. You will also need to run ``git pull`` from within every mod folder in order to update the modpack.
Check out the [Git SCM guide on submodules](https://git-scm.com/book/de/v2/Git-Tools-Submodule) for more information.

## Troubleshooting
Generally speaking, most mods should be compatible.

If you notice __odd movement speeds__ or jump heights of players, you should check for mods that also modify player physics. Use a compatibility mod like [player_monoids](https://github.com/minetest-mods/player_monoids) or [playerphysics](https://forum.minetest.net/viewtopic.php?t=22172) to get rid of this problem. This requires the conflicting mod to also support the chosen compatibility layer.

Mods that __modify the sky__ (including skybox, moon, sun, stars and clouds) are sadly not fully compatible because they conflict with Climate API's sky system. You should deactivate the sky features in either mod. You can do this in the climate_api mod configuration using the ``Override the skybox`` setting. If you're a mod maker then you can also optionally depend on climate_api and use ``climate_api.skybox.add_layer(playername, layer_name, options)`` to register your skybox change in a compatible way. Note that you need __at least Minetest v5.2.0__ for skybox changes to have any effect.

Conflicting skybox changes include the ``weather`` mod included in vanilla __Minetest Game__. You will want to disable that mod in order to use the more advanced cloud system introduced by Climate API. Head to ``Settings → All Settings → Games → Minetest Game`` and set ``Enable weather`` to ``Disabled``. This setting will only exist if you are using Minetest Game v5.2.0 or higher.

If you experience __performance issues__, the *Performance* section of Climate API's configuration section is a great place to start looking for a solution.

## Chat Commands
### Climate API
- ``/weather``: Display information on current weather effects. This command will show you current temperature and humidity, active weather presets and currently playing effects
- ``/weather_settings``: Display current mod configuration in the chat
- ``/weather_influences``: Display all different factors and how they affect you in this moment.
- ``/weather_status``: Display a list of all installed weather presets and whether they have been forced on, turned off, or are running normally (auto). If no weather presets are listed here then you need to install a weather mod like Regional Weather.
- ``/grant <playername> weather``: Enable a specified player to modify the current weather.
- ``/set_heat <value>``: Override global heat levels with given value.
- ``/set_base_heat <value>``: Override the base heat value used to calculate local climate. Positive numbers will increase temperature by X degrees Fahrenheit, whereas negative values will lower it.
- ``/set_humidity <value>``: Override global humidity levels with given value.
- ``/set_base_humidity <value>``: Override the base humidity value used to calculate local climate. Positive numbers will increase humidity by X percent, whereas negative values will lower it.
- ``/set_wind <x> <z>``: Override wind speed and direction. Higher absolute values result in stronger wind. The sign indicates direction.
- ``/set_weather <weather> <on|off|auto>``: Set a weather preset to always be applied (on), disable it completely (off), or reset it to be applied automatically (auto). Turning presets on manually might result in partially missing effects (like no sound if you enable sandstorms but no storms). Use ``/weather_status`` for a full list of installed weather presets. The prefix is important.

### Moon Phases
This mod comes with two commands to print or change the current moon phase.
- Use ``/moonphase`` to view the currently active phase.
- Use ``/set_moonphase <phase>`` to change it. ``<phase>`` has to be a full number between 1 and 8.
- Use ``/set_moonstyle <style>`` to choose a texture preset. ``classic`` will result in a quadratic moon
	inspired by default Minetest. ``realistic`` will result in 256x images of the real moon.

In order to change the phase, you will need the corresponding privilege.
Use ``/grant <player> moonphase`` to grant it.

## Configuration Options
The individual mods offer a lot of customization options. Make sure to check the respective README files for more information.

## Modding Information
Check the [api_doc.md](https://github.com/t-affeldt/climate_api/blob/master/api_doc.md) for a (currently incomplete) documentation on how to register new weather presets and visual effects. Use my weather [presets](https://github.com/t-affeldt/regional_weather/tree/master/ca_weathers) and [effects](https://github.com/t-affeldt/regional_weather/tree/master/ca_effects) as an example. Ask in the [forum](https://forum.minetest.net/viewtopic.php?t=24569) or open an [issue](https://github.com/t-affeldt/climate_api/issues) if you run into problems. Also check the source code of predefined weather effects because they include usage documentation at the top of each file.

## License
All parts of this modpack are using free software licenses.
Check the individual README and LICENSE files of each mod for information.

### Assets in screenshots
- Screenshots and editing: *CC BY-SA (3.0)* by me
- Logos and artwork: *CC BY-SA (3.0)* by Cap
- Lato Font (for the Logo): *OFL* by Łukasz Dziedzic from http://www.latofonts.com/lato-free-fonts/
- Source Sans Pro (for the subtitles): *OFL*, see https://fonts.google.com/specimen/Source+Sans+Pro
- Used texture pack: Polygonia (128px edition) *CC BY-SA (4.0)* by Lokrates. See https://forum.minetest.net/viewtopic.php?f=4&t=19043